62 Commits
Author SHA1 Message Date
marcel 65dbaad446 [update] adding possibility to display other waveform data (e.g. denoised/synthetic) together with genuine data for comparison 2024-03-22 17:12:04 +01:00
marcel 5b97d51517 [minor] mpl.figure.canvas.draw -> draw_idle 2024-03-22 17:12:04 +01:00
marcel f03ace75e7 [bugfix] QWidget.show() killed figure axis dimensions creating unexpected error of fig.aspect=0 when creating colorbar inset_axes in Python 3.11 2024-03-22 17:10:04 +01:00
marcel 9c78471d20 [bugfix] header resize method renamed in QT5 2024-03-22 15:34:05 +01:00
marcel 09d2fb1022 [bugfix] pt2 of fmpicker fix, make sure to also copy stream in autoPyLoT
closes #24
2023-08-24 12:55:30 +02:00
marcel 3cae6d3a78 [bugfix] use copies of wfdata when calling fmpicker to prevent modification of actual data used inside GUI 2023-08-24 11:28:30 +02:00
marcel 2e85d083a3 [bugfix] do not call calcsourcespec if incidence angle is outside bounds (for whatever reason) 2023-08-24 11:27:30 +02:00
marcel ba4e6cfe50 [bugfix] bin directory + /bin creates "/bin/bin". Also it is not taken care of os compatibility and also compatibility with existing code (line 86ff was useless after recent change in line 85) 2023-08-23 14:48:21 +02:00
marcel 1f16d01648 [minor] give more precise user warning if no pick channel was selected 2023-08-23 09:38:16 +02:00
marcel 3069e7d526 [minor] commented - possibly unnecessary - line of code that created an error when using old metadata Parser 2023-08-22 15:53:49 +02:00
marcel a9aeb7aaa3 [bugfix] set simple phase hint (P or S) 2023-08-22 15:53:49 +02:00
marcel b9adb182ad [bugfix] could not handle asterisk-marked events when opening tune-autopicker 2023-08-22 15:53:49 +02:00
marcel a823eb2440 [workaround] using explicit Exception definition without a special handling does not make sense. Function broke on other errors in polyfit. Still might need fixes in the two lines above the "except" block(s). 2023-08-22 15:53:49 +02:00
marcel 486e3dc9c3 Merge pull request 'Disabled button in case flag is false' (#31) from disable-show-log-widget into develop
Reviewed-on: #31
2023-08-22 12:05:33 +02:00
marcel 8d356050d7 [update] corrected original authors of PILOT 2023-08-22 12:01:51 +02:00
jeldrik 43cab3767f [Bugfix] fixxed wrong check for taupymodel 2023-06-27 08:04:00 +02:00
jeldrik a1f6c5ffca Bugfixxes, spectogram tab wip 2023-06-14 13:11:54 +02:00
jeldrik e4e7afa996 Minor changes to adjust to python 3. Temporary Fix for file exporting not working properly. WIP spectrogram view. 2023-04-27 10:24:55 +02:00
sebastianw 0634d24814 fix: disabled button in case flag is false
The button was not disabled in case the flag variable was false. The get_Bool function was renamed and improved to also work in case in the input variable is of type int or float.

Additionally, the environment file was corrected to also work for macOS installations with ARM architecture.
2023-04-06 16:40:20 +02:00
jeldrik 43c2b97b3d Small changes 2023-01-24 11:45:12 +01:00
ann-christin 8d94440e77 [bugfix] logwidget always initiated 2022-11-14 14:14:59 +01:00
ann-christin 66b7dea706 [update] pylot.in no longer mandatory 2022-11-14 14:14:12 +01:00
ann-christin ebf6d4806a [minor] reformating 2022-11-14 11:52:25 +01:00
ann-christin 207d0b3a6f [update] directly pass args from arg parser 2022-11-14 11:18:15 +01:00
ann-christin 3b3bbc29d1 Merge remote-tracking branch 'origin/develop' into develop 2022-11-14 10:30:38 +01:00
jeldrik 0c3fca9299 Re-Added local changes that had been lost due to technical problems ( no access to old machine ) 2022-10-04 11:44:31 +02:00
jeldrik 2d33a60421 [Bugfix] Multiple small bugfixxes keeping NLL from working in python3.+ 2022-09-15 14:31:13 +02:00
ann-christin a8c6f4c972 [reformat] spell checking 2022-08-25 15:31:08 +02:00
ann-christin 0d91f9e3fe update github link 2022-08-25 14:03:05 +02:00
ann-christin 494d281d61 update github link 2022-08-25 14:00:37 +02:00
kaan 5ef427ec12 Merge branch 'develop' of git.geophysik.ruhr-uni-bochum.de:marcel/pylot into develop 2022-06-29 14:07:35 +02:00
kaan 29cf978782 minor bug fixes 2022-06-29 14:07:06 +02:00
marcel 091449819c [update] tau-p usage for s-picking 2022-05-31 18:16:02 +02:00
marcel cd9c139349 [bugfix] mainly added missing taup model (lost in branch merging for python3), some smaller fixes for S picking 2022-05-31 12:45:22 +02:00
marcel 084fb10cea Merge remote-tracking branch 'origin/develop' into develop 2022-05-31 09:59:17 +02:00
marcel dde9520879 [minor] deactivate logwidget by default as it seems to irregularly create segfaults 2022-05-31 09:40:15 +02:00
marcel 7847f40a35 [minor] added developers do README 2022-04-01 12:07:51 +02:00
ludger 2c188432a1 Merge branch 'develop' of https://git.geophysik.ruhr-uni-bochum.de/marcel/pylot into develop 2022-03-24 13:28:52 +01:00
marcel 86dc0f5436 [minor] small style changes 2022-03-22 11:26:29 +01:00
marcel 83ba63a3fd Merge pull request 'feature/port-to-py3' (#11) from feature/port-to-py3 into develop
Reviewed-on: #11
2022-03-21 15:30:05 +01:00
ludger 3392100206 Removed psd 2022-03-17 13:42:27 +01:00
marcel 401265eb3a [minor] added some comments 2022-03-16 16:03:54 +01:00
marcel dd685d5d5e [refactor] rewrote/simplified getQualitiesfromxml code, used function already implemented in phases.py 2022-03-16 16:00:14 +01:00
marcel 3cd17ff364 [update] added xml file for unittest 2022-03-16 14:31:03 +01:00
marcel d879aa1a8b [bugfix] small fix to get getQualitiesfromxml running, added unittest for function 2022-03-16 14:29:47 +01:00
marcel 445f1da5ac [bugfix] reset stdout to previously set one and not to default sys.__stdout__ 2022-03-16 09:25:28 +01:00
marcel 962cf4edac [minor] README.md 2022-03-15 11:14:08 +01:00
marcel eb0dd87a9e [update] optimize requirements/yml file 2022-03-15 10:44:37 +01:00
marcel e9cc579cf6 [minor] README.md 2022-03-15 10:41:48 +01:00
marcel f1fd52b750 [bugfix] when closing mainwindow, also close logwidget 2022-03-15 10:41:29 +01:00
marcel 5449210797 [update] optimize requirements/yml file 2022-03-15 10:12:39 +01:00
marcel 0af948030b [bugfix] chooseArrival -> chooseArrivals, fixes #25 2022-03-15 09:42:25 +01:00
marcel 21b1be0e56 [update] updated README 2022-03-14 16:05:58 +01:00
marcel 58eee13b07 [update] removed setup.py (deprecated) 2022-03-14 15:53:42 +01:00
marcel c57eb4f556 [update] added requirements, updated pylot.yml and setup.py 2022-03-14 15:33:33 +01:00
marcel 47bb8c4326 Merge branch 'develop' into feature/port-to-py3 2022-03-14 13:19:20 +01:00
marcel 29ffcf2e37 [bugfix] pick_r unreferenced, closes #26 2022-03-14 11:18:51 +01:00
marcel e35d5d6df9 [refactor] automatic code reformatting (Pycharm) 2022-03-09 14:41:34 +01:00
marcel 79f3d40714 [refactor] code cleanup (WIP) 2022-03-09 14:28:30 +01:00
marcel 9cef22b74b [refactor] code cleanup (WIP), open issues #25 and #26 2022-03-09 14:08:04 +01:00
marcel c254ee09b3 [bugfix] updated some window flags for PySide2 2022-03-09 10:43:16 +01:00
marcel be32f4f61d [improvement] log can be opened from menu now (or focuses) 2022-03-09 10:42:52 +01:00
47 changed files with 113385 additions and 110381 deletions
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 MiB

+193 -104
View File
@@ -24,18 +24,16 @@ https://www.iconfinder.com/iconsets/flavour
""" """
import argparse import argparse
import matplotlib import json
import os import os
import platform import platform
import shutil import shutil
import sys import sys
import copy
import traceback import traceback
import json
from datetime import datetime from datetime import datetime
import matplotlib
matplotlib.use('Qt5Agg') matplotlib.use('Qt5Agg')
from PySide2 import QtGui, QtCore, QtWidgets from PySide2 import QtGui, QtCore, QtWidgets
@@ -44,8 +42,8 @@ from PySide2.QtCore import QCoreApplication, QSettings, Signal, QFile, \
from PySide2.QtGui import QIcon, QKeySequence, QPixmap, QStandardItem from PySide2.QtGui import QIcon, QKeySequence, QPixmap, QStandardItem
from PySide2.QtWidgets import QMainWindow, QInputDialog, QFileDialog, \ from PySide2.QtWidgets import QMainWindow, QInputDialog, QFileDialog, \
QWidget, QHBoxLayout, QVBoxLayout, QStyle, QLabel, QFrame, QAction, \ QWidget, QHBoxLayout, QVBoxLayout, QStyle, QLabel, QFrame, QAction, \
QDialog, QErrorMessage, QApplication, QMessageBox, QSplashScreen, \ QDialog, QApplication, QMessageBox, QSplashScreen, \
QActionGroup, QListWidget, QLineEdit, QListView, QAbstractItemView, \ QActionGroup, QListWidget, QListView, QAbstractItemView, \
QTreeView, QComboBox, QTabWidget, QPushButton, QGridLayout, QTableWidgetItem, QTableWidget QTreeView, QComboBox, QTabWidget, QPushButton, QGridLayout, QTableWidgetItem, QTableWidget
import numpy as np import numpy as np
from obspy import UTCDateTime, Stream from obspy import UTCDateTime, Stream
@@ -60,16 +58,15 @@ try:
from matplotlib.backends.backend_qt5agg import FigureCanvas from matplotlib.backends.backend_qt5agg import FigureCanvas
except ImportError: except ImportError:
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar
from matplotlib.figure import Figure from matplotlib.figure import Figure
from pylot.core.analysis.magnitude import LocalMagnitude, MomentMagnitude from pylot.core.analysis.magnitude import LocalMagnitude, MomentMagnitude, calcsourcespec
from pylot.core.io.data import Data from pylot.core.io.data import Data
from pylot.core.io.inputs import FilterOptions, PylotParameter from pylot.core.io.inputs import FilterOptions, PylotParameter
from autoPyLoT import autoPyLoT from autoPyLoT import autoPyLoT
from pylot.core.pick.compare import Comparison from pylot.core.pick.compare import Comparison
from pylot.core.pick.utils import symmetrize_error, getQualityFromUncertainty, getPickQuality, get_quality_class from pylot.core.pick.utils import getQualityFromUncertainty
from pylot.core.io.phases import picksdict_from_picks, picks_from_picksdict from pylot.core.io.phases import picksdict_from_picks
import pylot.core.loc.nll as nll import pylot.core.loc.nll as nll
from pylot.core.util.errors import DatastructureError, \ from pylot.core.util.errors import DatastructureError, \
OverwriteError OverwriteError
@@ -79,20 +76,21 @@ from pylot.core.util.utils import fnConstructor, getLogin, \
full_range, readFilterInformation, pick_color_plt, \ full_range, readFilterInformation, pick_color_plt, \
pick_linestyle_plt, identifyPhaseID, excludeQualityClasses, \ pick_linestyle_plt, identifyPhaseID, excludeQualityClasses, \
transform_colors_mpl, transform_colors_mpl_str, getAutoFilteroptions, check_all_obspy, \ transform_colors_mpl, transform_colors_mpl_str, getAutoFilteroptions, check_all_obspy, \
check_all_pylot, get_Bool, get_None, SetChannelComponents check_all_pylot, get_bool, get_None
from pylot.core.util.gui import make_pen from pylot.core.util.gui import make_pen
from pylot.core.util.event import Event from pylot.core.util.event import Event
from pylot.core.io.location import create_creation_info, create_event from pylot.core.io.location import create_creation_info, create_event
from pylot.core.util.widgets import FilterOptionsDialog, NewEventDlg, \ from pylot.core.util.widgets import FilterOptionsDialog, NewEventDlg, \
PylotCanvas, WaveformWidgetPG, PropertiesDlg, HelpForm, createAction, PickDlg, \ PylotCanvas, WaveformWidgetPG, PropertiesDlg, HelpForm, createAction, PickDlg, \
ComparisonWidget, TuneAutopicker, PylotParaBox, AutoPickDlg, CanvasWidget, AutoPickWidget, \ ComparisonWidget, TuneAutopicker, PylotParaBox, AutoPickDlg, CanvasWidget, AutoPickWidget, \
CompareEventsWidget, ProgressBarWidget, AddMetadataWidget, SingleTextLineDialog, LogWidget CompareEventsWidget, ProgressBarWidget, AddMetadataWidget, SingleTextLineDialog, LogWidget, PickQualitiesFromXml, \
SourceSpecWindow, ChooseWaveFormWindow, SpectrogramTab
from pylot.core.util.array_map import Array_map from pylot.core.util.array_map import Array_map
from pylot.core.util.structure import DATASTRUCTURE from pylot.core.util.structure import DATASTRUCTURE
from pylot.core.util.thread import Thread, Worker from pylot.core.util.thread import Thread, Worker
from pylot.core.util.version import get_git_version as _getVersionString from pylot.core.util.version import get_git_version as _getVersionString
from pylot.core.io.getEventListFromXML import geteventlistfromxml from pylot.core.io.getEventListFromXML import geteventlistfromxml
from pylot.core.io.getQualitiesfromxml import getQualitiesfromxml from pylot.core.io.phases import getQualitiesfromxml
from pylot.styles import style_settings from pylot.styles import style_settings
@@ -119,17 +117,19 @@ class MainWindow(QMainWindow):
if not infile: if not infile:
infile = os.path.join(os.path.expanduser('~'), '.pylot', 'pylot.in') infile = os.path.join(os.path.expanduser('~'), '.pylot', 'pylot.in')
print('Using default input file {}'.format(infile)) print('Using default input file {}'.format(infile))
if os.path.isfile(infile) == False: if os.path.isfile(infile) is False:
infile = QFileDialog().getOpenFileName(caption='Choose PyLoT-input file') infile = QFileDialog().getOpenFileName(caption='Choose PyLoT-input file')[0]
if not os.path.exists(infile[0]): if not os.path.exists(infile):
QMessageBox.warning(self, "PyLoT Warning", QMessageBox.warning(self, "PyLoT Warning",
"No PyLoT-input file declared!") "No PyLoT-input file declared! Using default parameters!")
sys.exit(0) infile = None
self.infile = infile[0]
else:
self.infile = infile
self._inputs = PylotParameter(infile) self._inputs = PylotParameter(infile)
if not infile:
self._inputs.reset_defaults()
self.infile = infile
self._props = None self._props = None
self.gain = 1. self.gain = 1.
@@ -181,6 +181,7 @@ class MainWindow(QMainWindow):
self.autodata = Data(self) self.autodata = Data(self)
self.fnames = None self.fnames = None
self.fnames_comp = None
self._stime = None self._stime = None
# track deleted picks for logging # track deleted picks for logging
@@ -257,7 +258,7 @@ class MainWindow(QMainWindow):
self._inputs.export2File(infile) self._inputs.export2File(infile)
self.infile = infile self.infile = infile
def setupUi(self): def setupUi(self, use_logwidget=False):
try: try:
self.startTime = min( self.startTime = min(
[tr.stats.starttime for tr in self.data.wfdata]) [tr.stats.starttime for tr in self.data.wfdata])
@@ -436,9 +437,9 @@ class MainWindow(QMainWindow):
None, paraIcon, None, paraIcon,
"Modify Parameter") "Modify Parameter")
self.deleteAutopicksAction = self.createAction(self, "Delete Autopicks", self.deleteAutopicksAction = self.createAction(self, "Delete Autopicks",
self.deleteAllAutopicks, self.deleteAllAutopicks,
None, deleteIcon, None, deleteIcon,
"Delete all automatic picks from Project.") "Delete all automatic picks from Project.")
self.filterActionP = createAction(parent=self, text='Apply P Filter', self.filterActionP = createAction(parent=self, text='Apply P Filter',
slot=self.filterP, slot=self.filterP,
icon=self.filter_icon_p, icon=self.filter_icon_p,
@@ -483,7 +484,7 @@ class MainWindow(QMainWindow):
"automatic pick " "automatic pick "
"data.", False) "data.", False)
self.compare_action.setEnabled(False) self.compare_action.setEnabled(False)
self.qualities_action = self.createAction(parent=self, text='Show pick qualitites...', self.qualities_action = self.createAction(parent=self, text='Show pick qualities...',
slot=self.pickQualities, shortcut='Alt+Q', slot=self.pickQualities, shortcut='Alt+Q',
icon=qualities_icon, tip='Histogram of pick qualities') icon=qualities_icon, tip='Histogram of pick qualities')
self.qualities_action.setEnabled(False) self.qualities_action.setEnabled(False)
@@ -492,10 +493,10 @@ class MainWindow(QMainWindow):
self.qualities_action.setVisible(True) self.qualities_action.setVisible(True)
self.eventlist_xml_action = self.createAction(parent=self, text='Create Eventlist from XML', self.eventlist_xml_action = self.createAction(parent=self, text='Create Eventlist from XML',
slot=self.eventlistXml, shortcut='Alt+X', slot=self.eventlistXml, shortcut='Alt+X',
icon=eventlist_xml_icon, tip='Create an Eventlist from a XML File') icon=eventlist_xml_icon,
tip='Create an Eventlist from a XML File')
self.eventlist_xml_action.setEnabled(False) self.eventlist_xml_action.setEnabled(False)
printAction = self.createAction(self, "&Print event ...", printAction = self.createAction(self, "&Print event ...",
self.show_event_information, QKeySequence.Print, self.show_event_information, QKeySequence.Print,
print_icon, print_icon,
@@ -505,6 +506,10 @@ class MainWindow(QMainWindow):
"""Show either the documentation """Show either the documentation
homepage (internet connection available), homepage (internet connection available),
or shipped documentation files.""") or shipped documentation files.""")
logAction = self.createAction(self, "&Show Log", self.showLogWidget,
tip="""Display Log""")
logAction.setEnabled(use_logwidget)
# create button group for component selection # create button group for component selection
@@ -559,7 +564,8 @@ class MainWindow(QMainWindow):
' the complete project on grid engine.') ' the complete project on grid engine.')
self.auto_pick_sge.setEnabled(False) self.auto_pick_sge.setEnabled(False)
pickActions = (self.auto_tune, self.auto_pick, self.compare_action, self.qualities_action, self.eventlist_xml_action) pickActions = (
self.auto_tune, self.auto_pick, self.compare_action, self.qualities_action, self.eventlist_xml_action)
# pickToolBar = self.addToolBar("PickTools") # pickToolBar = self.addToolBar("PickTools")
# pickToolActions = (selectStation, ) # pickToolActions = (selectStation, )
@@ -570,7 +576,7 @@ class MainWindow(QMainWindow):
shortcut='Alt+Ctrl+L', shortcut='Alt+Ctrl+L',
icon=locate_icon, icon=locate_icon,
tip='Locate the event using ' tip='Locate the event using '
'the displayed manual arrivals.') 'the displayed manual arrivals.')
self.locateEventAction.setEnabled(False) self.locateEventAction.setEnabled(False)
locationToolActions = (self.locateEventAction,) locationToolActions = (self.locateEventAction,)
@@ -600,11 +606,10 @@ class MainWindow(QMainWindow):
self.autoPickMenu = self.pickMenu.addMenu(self.autopicksicon_small, 'Automatic picking') self.autoPickMenu = self.pickMenu.addMenu(self.autopicksicon_small, 'Automatic picking')
self.autoPickMenu.setEnabled(False) self.autoPickMenu.setEnabled(False)
autoPickActions = (self.auto_pick, self.auto_pick_local, self.auto_pick_sge) autoPickActions = (self.auto_pick, self.auto_pick_local, self.auto_pick_sge)
self.helpMenu = self.menuBar().addMenu('&Help') self.helpMenu = self.menuBar().addMenu('&Help')
helpActions = (helpAction,) helpActions = (helpAction, logAction)
fileToolActions = (self.newProjectAction, fileToolActions = (self.newProjectAction,
self.openProjectAction, self.saveProjectAction, self.openProjectAction, self.saveProjectAction,
@@ -695,14 +700,17 @@ class MainWindow(QMainWindow):
wf_tab = QtWidgets.QWidget(self) wf_tab = QtWidgets.QWidget(self)
array_tab = QtWidgets.QWidget(self) array_tab = QtWidgets.QWidget(self)
events_tab = QtWidgets.QWidget(self) events_tab = QtWidgets.QWidget(self)
spectro_tab = QtWidgets.QWidget(self)
# init main widgets layouts # init main widgets layouts
self.wf_layout = QtWidgets.QVBoxLayout() self.wf_layout = QtWidgets.QVBoxLayout()
self.array_layout = QtWidgets.QVBoxLayout() self.array_layout = QtWidgets.QVBoxLayout()
self.events_layout = QtWidgets.QVBoxLayout() self.events_layout = QtWidgets.QVBoxLayout()
self.spectro_layout = QtWidgets.QVBoxLayout()
wf_tab.setLayout(self.wf_layout) wf_tab.setLayout(self.wf_layout)
array_tab.setLayout(self.array_layout) array_tab.setLayout(self.array_layout)
events_tab.setLayout(self.events_layout) events_tab.setLayout(self.events_layout)
spectro_tab.setLayout(self.spectro_layout)
# tighten up layouts inside tabs # tighten up layouts inside tabs
for layout in [self.wf_layout, self.array_layout, self.events_layout]: for layout in [self.wf_layout, self.array_layout, self.events_layout]:
@@ -713,12 +721,14 @@ class MainWindow(QMainWindow):
self.tabs.addTab(wf_tab, 'Waveform Plot') self.tabs.addTab(wf_tab, 'Waveform Plot')
self.tabs.addTab(array_tab, 'Array Map') self.tabs.addTab(array_tab, 'Array Map')
self.tabs.addTab(events_tab, 'Eventlist') self.tabs.addTab(events_tab, 'Eventlist')
self.tabs.addTab(spectro_tab, 'Spectro')
self.wf_layout.addWidget(self.no_data_label) self.wf_layout.addWidget(self.no_data_label)
self.wf_layout.addWidget(self.wf_scroll_area) self.wf_layout.addWidget(self.wf_scroll_area)
self.wf_scroll_area.setWidgetResizable(True) self.wf_scroll_area.setWidgetResizable(True)
self.init_array_tab() self.init_array_tab()
self.init_event_table() self.init_event_table()
self.init_spectro_tab()
self.tabs.setCurrentIndex(0) self.tabs.setCurrentIndex(0)
self.eventLabel = QLabel() self.eventLabel = QLabel()
@@ -731,13 +741,19 @@ class MainWindow(QMainWindow):
_widget.setLayout(self._main_layout) _widget.setLayout(self._main_layout)
_widget.showFullScreen() _widget.showFullScreen()
self.logwidget = LogWidget(parent=None)
self.logwidget.show() if use_logwidget:
sys.stdout = self.logwidget.stdout self.logwidget = LogWidget(parent=None)
sys.stderr = self.logwidget.stderr self.logwidget.show()
self.stdout = self.logwidget.stdout
self.stderr = self.logwidget.stderr
self.setCentralWidget(_widget) self.setCentralWidget(_widget)
# Need to store PickQualities Window somewhere so it doesnt disappear
self.pickQualitiesWindow = None
def init_wfWidget(self): def init_wfWidget(self):
xlab = self.startTime.strftime('seconds since %Y/%m/%d %H:%M:%S (%Z)') xlab = self.startTime.strftime('seconds since %Y/%m/%d %H:%M:%S (%Z)')
plottitle = None # "Overview: {0} components ".format(self.getComponent()) plottitle = None # "Overview: {0} components ".format(self.getComponent())
@@ -771,6 +787,10 @@ class MainWindow(QMainWindow):
self.ref_event_button.setEnabled(False) self.ref_event_button.setEnabled(False)
self.test_event_button.setEnabled(False) self.test_event_button.setEnabled(False)
def showLogWidget(self):
self.logwidget.show()
self.logwidget.activateWindow()
def keyPressEvent(self, event): def keyPressEvent(self, event):
if event.key() == QtCore.Qt.Key.Key_Control: if event.key() == QtCore.Qt.Key.Key_Control:
self._ctrl = True self._ctrl = True
@@ -802,7 +822,7 @@ class MainWindow(QMainWindow):
def modify_gain(self, direction, factor): def modify_gain(self, direction, factor):
assert (direction in ['+', '-']), 'unknown direction' assert (direction in ['+', '-']), 'unknown direction'
if self._ctrl: if self._ctrl:
factor = factor**3 factor = factor ** 3
if direction == '+': if direction == '+':
self.gain *= factor self.gain *= factor
elif direction == '-': elif direction == '-':
@@ -954,7 +974,6 @@ class MainWindow(QMainWindow):
self.recentProjectsMenu.addAction(action) self.recentProjectsMenu.addAction(action)
@property @property
def inputs(self): def inputs(self):
return self._inputs return self._inputs
@@ -992,7 +1011,7 @@ class MainWindow(QMainWindow):
if not sld.exec_(): if not sld.exec_():
return return
fext = sld.lineEdit.text() fext = sld.lineEdit.text()
#fext = '.xml' # fext = '.xml'
for event in events: for event in events:
path = event.path path = event.path
eventname = path.split('/')[-1] # or event.pylot_id eventname = path.split('/')[-1] # or event.pylot_id
@@ -1024,7 +1043,7 @@ class MainWindow(QMainWindow):
data_new = Data(self, evtdata=str(fname)) data_new = Data(self, evtdata=str(fname))
# MP MP commented because adding several picks might cause inconsistencies # MP MP commented because adding several picks might cause inconsistencies
data = data_new data = data_new
#data += data_new # data += data_new
except ValueError: except ValueError:
qmb = QMessageBox(self, icon=QMessageBox.Question, qmb = QMessageBox(self, icon=QMessageBox.Question,
text='Warning: Missmatch in event identifiers {} and {}. Continue?'.format( text='Warning: Missmatch in event identifiers {} and {}. Continue?'.format(
@@ -1112,16 +1131,19 @@ class MainWindow(QMainWindow):
else: else:
return return
def getWFFnames_from_eventbox(self, eventbox=None): def getWFFnames_from_eventbox(self, eventbox: str = None, subpath: str = None) -> list:
''' '''
Return waveform filenames from event in eventbox. Return waveform filenames from event in eventbox.
''' '''
# TODO: add dataStructure class for obspyDMT here, this is just a workaround! # TODO: add dataStructure class for obspyDMT here, this is just a workaround!
eventpath = self.get_current_event_path(eventbox) eventpath = self.get_current_event_path(eventbox)
basepath = eventpath.split(os.path.basename(eventpath))[0] if subpath:
eventpath = os.path.join(eventpath, subpath)
if not os.path.isdir(eventpath):
return []
if self.dataStructure: if self.dataStructure:
if not eventpath: if not eventpath:
return return []
fnames = [os.path.join(eventpath, f) for f in os.listdir(eventpath)] fnames = [os.path.join(eventpath, f) for f in os.listdir(eventpath)]
else: else:
raise DatastructureError('not specified') raise DatastructureError('not specified')
@@ -1165,7 +1187,7 @@ class MainWindow(QMainWindow):
''' '''
if not self.project: if not self.project:
self.createNewProject() self.createNewProject()
ed = getExistingDirectories(self, 'Select event directories...') ed = GetExistingDirectories(self, 'Select event directories...')
if ed.exec_(): if ed.exec_():
eventlist = [event for event in ed.selectedFiles() if not event.endswith('EVENTS-INFO')] eventlist = [event for event in ed.selectedFiles() if not event.endswith('EVENTS-INFO')]
basepath = eventlist[0].split(os.path.basename(eventlist[0]))[0] basepath = eventlist[0].split(os.path.basename(eventlist[0]))[0]
@@ -1173,7 +1195,8 @@ class MainWindow(QMainWindow):
eventlist_file = os.path.join(basepath, 'eventlist.txt') eventlist_file = os.path.join(basepath, 'eventlist.txt')
if os.path.isfile(eventlist_file): if os.path.isfile(eventlist_file):
with open(eventlist_file, 'r') as infile: with open(eventlist_file, 'r') as infile:
eventlist_subset = [os.path.join(basepath, filename.split('\n')[0]) for filename in infile.readlines()] eventlist_subset = [os.path.join(basepath, filename.split('\n')[0]) for filename in
infile.readlines()]
msg = 'Found file "eventlist.txt" in database path. WILL ONLY USE SELECTED EVENTS out of {} events ' \ msg = 'Found file "eventlist.txt" in database path. WILL ONLY USE SELECTED EVENTS out of {} events ' \
'contained in this subset' 'contained in this subset'
print(msg.format(len(eventlist_subset))) print(msg.format(len(eventlist_subset)))
@@ -1224,7 +1247,7 @@ class MainWindow(QMainWindow):
print('Warning: Could not automatically init folder structure. ({})'.format(e)) print('Warning: Could not automatically init folder structure. ({})'.format(e))
settings = QSettings() settings = QSettings()
settings.setValue("data/dataRoot", dirs['datapath']) #d irs['rootpath']) settings.setValue("data/dataRoot", dirs['datapath']) # d irs['rootpath'])
settings.sync() settings.sync()
if not self.project.eventlist: if not self.project.eventlist:
@@ -1365,7 +1388,7 @@ class MainWindow(QMainWindow):
index = eventBox.currentIndex() index = eventBox.currentIndex()
tv = QtWidgets.QTableView() tv = QtWidgets.QTableView()
header = tv.horizontalHeader() header = tv.horizontalHeader()
header.setResizeMode(QtWidgets.QHeaderView.ResizeToContents) header.setSectionResizeMode(QtWidgets.QHeaderView.ResizeToContents)
header.setStretchLastSection(True) header.setStretchLastSection(True)
header.hide() header.hide()
tv.verticalHeader().hide() tv.verticalHeader().hide()
@@ -1538,6 +1561,7 @@ class MainWindow(QMainWindow):
event = self.get_current_event() event = self.get_current_event()
if not type(outformats) == list: if not type(outformats) == list:
outformats = [outformats] outformats = [outformats]
def getSavePath(event, directory, outformats): def getSavePath(event, directory, outformats):
if not directory: if not directory:
title = 'Save event data as {} to directory ...'.format(outformats) title = 'Save event data as {} to directory ...'.format(outformats)
@@ -1660,16 +1684,55 @@ class MainWindow(QMainWindow):
self.cmpw.show() self.cmpw.show()
def pickQualities(self): def pickQualities(self):
path = self._inputs['rootpath'] + '/' + self._inputs['datapath'] + '/' + self._inputs['database'] path = self.get_current_event_path()
getQualitiesfromxml(path) (_, plot) = getQualitiesfromxml(path, self._inputs.get('timeerrorsP'), self._inputs.get('timeerrorsS'),plotflag=1)
self.pickQualitiesWindow = PickQualitiesFromXml(figure=plot, path=self.get_current_event_path(),inputVar=self._inputs)
self.pickQualitiesWindow.showUI()
return return
# WIP JG
def eventlistXml(self): def eventlistXml(self):
path = self._inputs['rootpath'] + '/' + self._inputs['datapath'] + '/' + self._inputs['database'] path = self._inputs['rootpath'] + '/' + self._inputs['datapath'] + '/' + self._inputs['database']
outpath = self.project.location[:self.project.location.rfind('/')] outpath = self.project.location[:self.project.location.rfind('/')]
geteventlistfromxml(path, outpath) geteventlistfromxml(path, outpath)
return return
# WIP JG
def spectogramView(self):
global test
stations = []
names = []
traces = {}
for tr in self.get_data().wfdata.traces:
if not tr.stats.station in stations:
stations.append(tr.stats.station)
names.append(tr.stats.network + '.' + tr.stats.station)
for station in stations:
traces[station] = {}
for ch in ['Z', 'N', 'E']:
for tr in self.get_data().wfdata.select(component=ch).traces:
traces[tr.stats.station][ch] = tr
names.sort()
a = self.get_current_event()
print (self.get_data().wfdata.traces[0])
test = SpectrogramTab(traces, self.get_data().wfdata)
height = self.tabs.widget(0).height()
width = self.tabs.widget(0).width()
self.tabs.setCurrentIndex(3)
figCanvas = test.makeSpecFig(direction=self.dispComponent, height = height, width = width, parent = self.tabs.widget)
return figCanvas
#self.spectro_layout.addWidget()
# self.get_data().wfdata.spectrogram()
# self.tabs.addTab(figCanvas, 'Spectrogram')
# self.tabs[3] = figCanvas
# self.refreshTabs()
# test.show()
def compareMulti(self): def compareMulti(self):
if not self.compareoptions: if not self.compareoptions:
return return
@@ -1855,6 +1918,13 @@ class MainWindow(QMainWindow):
self.newWF(plot=False) self.newWF(plot=False)
self.update_obspy_dmt() self.update_obspy_dmt()
self.refresh_array_map() self.refresh_array_map()
if self.tabs.currentIndex() == 3:
if self.spectroWidget != None:
self.spectro_layout.removeWidget(self.spectroWidget)
newSpectroWidget = self.spectogramView()
self.spectro_layout.addWidget(newSpectroWidget)
self.spectroWidget = newSpectroWidget
def newWF(self, event=None, plot=True): def newWF(self, event=None, plot=True):
''' '''
@@ -1885,13 +1955,20 @@ class MainWindow(QMainWindow):
def prepareLoadWaveformData(self): def prepareLoadWaveformData(self):
self.fnames = self.getWFFnames_from_eventbox() self.fnames = self.getWFFnames_from_eventbox()
self.fnames_syn = [] self.fnames_comp = []
fnames_comp = self.getWFFnames_from_eventbox(subpath='compare')
self.dataPlot.activateCompareOptions(bool(fnames_comp))
if fnames_comp:
if self.dataPlot.comp_checkbox.isChecked():
self.fnames_comp = fnames_comp
eventpath = self.get_current_event_path() eventpath = self.get_current_event_path()
basepath = eventpath.split(os.path.basename(eventpath))[0] basepath = eventpath.split(os.path.basename(eventpath))[0]
self.obspy_dmt = check_obspydmt_structure(basepath) self.obspy_dmt = check_obspydmt_structure(basepath)
self.dataPlot.activateObspyDMToptions(self.obspy_dmt) self.dataPlot.activateObspyDMToptions(self.obspy_dmt)
if self.obspy_dmt: if self.obspy_dmt:
self.prepareObspyDMT_data(eventpath) self.prepareObspyDMT_data(eventpath)
self.dataPlot.activateCompareOptions(True)
def loadWaveformData(self): def loadWaveformData(self):
''' '''
@@ -1922,15 +1999,15 @@ class MainWindow(QMainWindow):
tstop = None tstop = None
self.data.setWFData(self.fnames, self.data.setWFData(self.fnames,
self.fnames_syn, self.fnames_comp,
checkRotated=True, checkRotated=True,
metadata=self.metadata, metadata=self.metadata,
tstart=tstart, tstart=tstart,
tstop=tstop,) tstop=tstop, )
def prepareObspyDMT_data(self, eventpath): def prepareObspyDMT_data(self, eventpath):
qcbox_processed = self.dataPlot.qcombo_processed qcbox_processed = self.dataPlot.qcombo_processed
qcheckb_syn = self.dataPlot.syn_checkbox qcheckb_syn = self.dataPlot.comp_checkbox
qcbox_processed.setEnabled(False) qcbox_processed.setEnabled(False)
qcheckb_syn.setEnabled(False) qcheckb_syn.setEnabled(False)
for fpath in os.listdir(eventpath): for fpath in os.listdir(eventpath):
@@ -1938,8 +2015,8 @@ class MainWindow(QMainWindow):
if 'syngine' in fpath: if 'syngine' in fpath:
eventpath_syn = os.path.join(eventpath, fpath) eventpath_syn = os.path.join(eventpath, fpath)
qcheckb_syn.setEnabled(True) qcheckb_syn.setEnabled(True)
if self.dataPlot.syn_checkbox.isChecked(): if self.dataPlot.comp_checkbox.isChecked():
self.fnames_syn = [os.path.join(eventpath_syn, filename) for filename in os.listdir(eventpath_syn)] self.fnames_comp = [os.path.join(eventpath_syn, filename) for filename in os.listdir(eventpath_syn)]
if 'processed' in fpath: if 'processed' in fpath:
qcbox_processed.setEnabled(True) qcbox_processed.setEnabled(True)
if qcbox_processed.isEnabled(): if qcbox_processed.isEnabled():
@@ -2112,6 +2189,7 @@ class MainWindow(QMainWindow):
self.locateEventAction.setEnabled(True) self.locateEventAction.setEnabled(True)
self.qualities_action.setEnabled(True) self.qualities_action.setEnabled(True)
self.eventlist_xml_action.setEnabled(True) self.eventlist_xml_action.setEnabled(True)
if True in self.comparable.values(): if True in self.comparable.values():
self.compare_action.setEnabled(True) self.compare_action.setEnabled(True)
self.draw() self.draw()
@@ -2123,7 +2201,7 @@ class MainWindow(QMainWindow):
if self.obspy_dmt: if self.obspy_dmt:
invpath = os.path.join(self.get_current_event_path(), 'resp') invpath = os.path.join(self.get_current_event_path(), 'resp')
if not invpath in self.metadata.inventories: if not invpath in self.metadata.inventories:
self.metadata.add_inventory(invpath, obspy_dmt_inv = True) self.metadata.add_inventory(invpath, obspy_dmt_inv=True)
# check if directory is empty # check if directory is empty
if os.listdir(invpath): if os.listdir(invpath):
self.init_map_button.setEnabled(True) self.init_map_button.setEnabled(True)
@@ -2219,7 +2297,7 @@ class MainWindow(QMainWindow):
comp = self.getComponent() comp = self.getComponent()
title = 'section: {0} components'.format(zne_text[comp]) title = 'section: {0} components'.format(zne_text[comp])
wfst = self.get_data().getWFData() wfst = self.get_data().getWFData()
wfsyn = self.get_data().getSynWFData() wfsyn = self.get_data().getAltWFdata()
if self.filterActionP.isChecked() and filter: if self.filterActionP.isChecked() and filter:
self.filterWaveformData(plot=False, phase='P') self.filterWaveformData(plot=False, phase='P')
elif self.filterActionS.isChecked() and filter: elif self.filterActionS.isChecked() and filter:
@@ -2228,7 +2306,7 @@ class MainWindow(QMainWindow):
# wfst += self.get_data().getWFData().select(component=alter_comp) # wfst += self.get_data().getWFData().select(component=alter_comp)
plotWidget = self.getPlotWidget() plotWidget = self.getPlotWidget()
self.adjustPlotHeight() self.adjustPlotHeight()
if get_Bool(settings.value('large_dataset')) == True: if get_bool(settings.value('large_dataset')):
self.plot_method = 'fast' self.plot_method = 'fast'
else: else:
self.plot_method = 'normal' self.plot_method = 'normal'
@@ -2531,17 +2609,21 @@ class MainWindow(QMainWindow):
print("Warning! No network, station, and location info available!") print("Warning! No network, station, and location info available!")
return return
self.update_status('picking on station {0}'.format(station)) self.update_status('picking on station {0}'.format(station))
data = self.get_data().getOriginalWFData().copy() wfdata = self.get_data().getOriginalWFData().copy()
wfdata_comp = self.get_data().getAltWFdata().copy()
event = self.get_current_event() event = self.get_current_event()
wftype = self.dataPlot.qcombo_processed.currentText() if self.obspy_dmt else None wftype = self.dataPlot.qcombo_processed.currentText() if self.obspy_dmt else None
pickDlg = PickDlg(self, parameter=self._inputs, pickDlg = PickDlg(self, parameter=self._inputs,
data=data.select(station=station), data=wfdata.select(station=station),
data_compare=wfdata_comp.select(station=station),
station=station, network=network, station=station, network=network,
location=location, location=location,
picks=self.getPicksOnStation(station, 'manual'), picks=self.getPicksOnStation(station, 'manual'),
autopicks=self.getPicksOnStation(station, 'auto'), autopicks=self.getPicksOnStation(station, 'auto'),
metadata=self.metadata, event=event, metadata=self.metadata, event=event,
filteroptions=self.filteroptions, wftype=wftype) model=self.inputs.get('taup_model'),
filteroptions=self.filteroptions, wftype=wftype,
show_comp_data=self.dataPlot.comp_checkbox.isChecked())
if self.filterActionP.isChecked(): if self.filterActionP.isChecked():
pickDlg.currentPhase = "P" pickDlg.currentPhase = "P"
pickDlg.filterWFData() pickDlg.filterWFData()
@@ -2654,7 +2736,7 @@ class MainWindow(QMainWindow):
self.init_fig_dict() self.init_fig_dict()
# if not self.tap: # if not self.tap:
# init TuneAutopicker object # init TuneAutopicker object
self.tap = TuneAutopicker(self) self.tap = TuneAutopicker(self, self.obspy_dmt)
# first call of update to init tabs with empty canvas # first call of update to init tabs with empty canvas
self.update_autopicker() self.update_autopicker()
# connect update signal of TuneAutopicker with update function # connect update signal of TuneAutopicker with update function
@@ -2696,9 +2778,9 @@ class MainWindow(QMainWindow):
# init event selection options for autopick # init event selection options for autopick
self.pickoptions = [('current event', self.get_current_event, None), self.pickoptions = [('current event', self.get_current_event, None),
('tune events', self.get_ref_events, self._style['ref']['rgba']), ('tune events', self.get_ref_events, self._style['ref']['rgba']),
('test events', self.get_test_events, self._style['test']['rgba']),] ('test events', self.get_test_events, self._style['test']['rgba']), ]
#('all (picked) events', self.get_manu_picked_events, None), # ('all (picked) events', self.get_manu_picked_events, None),
#('all events', self.get_all_events, None)] # ('all events', self.get_all_events, None)]
self.listWidget = QListWidget() self.listWidget = QListWidget()
self.setDirty(True) self.setDirty(True)
@@ -2853,7 +2935,9 @@ class MainWindow(QMainWindow):
self.log_deleted_picks([deleted_pick]) self.log_deleted_picks([deleted_pick])
def log_deleted_picks(self, deleted_picks, event_path=None): def log_deleted_picks(self, deleted_picks, event_path=None):
''' Log deleted picks to list self.deleted_picks ''' '''
Log deleted picks to list self.deleted_picks
'''
if not event_path: if not event_path:
event_path = self.get_current_event_path() event_path = self.get_current_event_path()
for deleted_pick in deleted_picks: for deleted_pick in deleted_picks:
@@ -2867,7 +2951,9 @@ class MainWindow(QMainWindow):
self.deleted_picks[event_path].append(deleted_pick) self.deleted_picks[event_path].append(deleted_pick)
def dump_deleted_picks(self, event_path): def dump_deleted_picks(self, event_path):
''' Save deleted picks to json file for event in event_path. Load old file before and merge''' '''
Save deleted picks to json file for event in event_path. Load old file before and merge
'''
try: try:
deleted_picks_from_file = self.load_deleted_picks(event_path) deleted_picks_from_file = self.load_deleted_picks(event_path)
except Exception as e: except Exception as e:
@@ -2896,7 +2982,7 @@ class MainWindow(QMainWindow):
def safetyCopy(self, event_path): def safetyCopy(self, event_path):
fpath = self.get_deleted_picks_fpath(event_path) fpath = self.get_deleted_picks_fpath(event_path)
fpath_new = fpath.split('.json')[0] + '_copy_{}.json'.format(datetime.now()).replace(' ', '_') fpath_new = fpath.split('.json')[0] + '_copy_{}.json'.format(datetime.now()).replace(' ', '_')
shutil.move(fpath, fpath_new) shutil.move(fpath, fpath_new)
def load_deleted_picks(self, event_path): def load_deleted_picks(self, event_path):
@@ -3063,11 +3149,13 @@ class MainWindow(QMainWindow):
lt = locateTool[loctool] lt = locateTool[loctool]
# get working directory # get working directory
locroot = parameter['nllocroot'] locroot = parameter['nllocroot']
#locroot = 'E:/NLL/src/Insheim'
if locroot is None: if locroot is None:
self.PyLoTprefs() self.PyLoTprefs()
self.locate_event() self.locate_event()
ctrfile = os.path.join(locroot, 'run', parameter['ctrfile']) ctrfile = os.path.join(locroot, 'run', parameter['ctrfile'])
#ctrfile = 'E:/NLL/src/Insheim/run/Insheim_min1d032016.in'
ttt = parameter['ttpatter'] ttt = parameter['ttpatter']
outfile = parameter['outpatter'] outfile = parameter['outpatter']
eventname = self.get_current_event_name() eventname = self.get_current_event_name()
@@ -3078,7 +3166,7 @@ class MainWindow(QMainWindow):
phasefile = os.path.join(obsdir, filename + '.obs') phasefile = os.path.join(obsdir, filename + '.obs')
lt.modify_inputs(ctrfile, locroot, filename, phasefile, ttt) lt.modify_inputs(ctrfile, locroot, filename, phasefile, ttt)
try: try:
lt.locate(ctrfile) lt.locate(ctrfile, self._inputs)
except RuntimeError as e: except RuntimeError as e:
print(e.message) print(e.message)
# finally: # finally:
@@ -3130,9 +3218,9 @@ class MainWindow(QMainWindow):
Try to init array map widget. If no metadata are given, Try to init array map widget. If no metadata are given,
self.get_metadata will be called. self.get_metadata will be called.
''' '''
if checked: pass # dummy argument for QAction trigger signal if checked: pass # dummy argument for QAction trigger signal
self.tabs.setCurrentIndex(1) self.tabs.setCurrentIndex(1)
# if there is no metadata (invetories is an empty list), just initialize the default empty tab # if there is no metadata (inventories is an empty list), just initialize the default empty tab
if not self.metadata.inventories: if not self.metadata.inventories:
self.init_array_tab() self.init_array_tab()
return return
@@ -3154,6 +3242,15 @@ class MainWindow(QMainWindow):
self.tabs.setCurrentIndex(index) self.tabs.setCurrentIndex(index)
self.refresh_array_map() self.refresh_array_map()
def init_spectro_tab(self):
'''
Init spectrogram tab with currently selected event.
'''
self.spectroWidget = None
#self.spectro_layout.addWidget( self.spectogramView() )
pass
def array_map_thread(self): def array_map_thread(self):
''' '''
Start modal thread to init the array_map object. Start modal thread to init the array_map object.
@@ -3293,9 +3390,9 @@ class MainWindow(QMainWindow):
item_depth = QTableWidgetItem() item_depth = QTableWidgetItem()
item_momentmag = QTableWidgetItem() item_momentmag = QTableWidgetItem()
item_localmag = QTableWidgetItem() item_localmag = QTableWidgetItem()
item_nmp = QTableWidgetItem('{}'.format(ma_count['manual']))#, ma_count_total['manual'])) item_nmp = QTableWidgetItem('{}'.format(ma_count['manual'])) # , ma_count_total['manual']))
item_nmp.setIcon(self.manupicksicon_small) item_nmp.setIcon(self.manupicksicon_small)
item_nap = QTableWidgetItem('{}'.format(ma_count['auto']))#, ma_count_total['auto'])) item_nap = QTableWidgetItem('{}'.format(ma_count['auto'])) # , ma_count_total['auto']))
item_nap.setIcon(self.autopicksicon_small) item_nap.setIcon(self.autopicksicon_small)
item_ref = QTableWidgetItem() item_ref = QTableWidgetItem()
item_test = QTableWidgetItem() item_test = QTableWidgetItem()
@@ -3376,7 +3473,7 @@ class MainWindow(QMainWindow):
self.event_table.setCellWidget(r_index, c_index, item) self.event_table.setCellWidget(r_index, c_index, item)
header = self.event_table.horizontalHeader() header = self.event_table.horizontalHeader()
header.setResizeMode(QtWidgets.QHeaderView.ResizeToContents) header.setSectionResizeMode(QtWidgets.QHeaderView.ResizeToContents)
header.setStretchLastSection(True) header.setStretchLastSection(True)
self.event_table.cellChanged[int, int].connect(cell_changed) self.event_table.cellChanged[int, int].connect(cell_changed)
self.event_table.cellClicked[int, int].connect(cell_clicked) self.event_table.cellClicked[int, int].connect(cell_clicked)
@@ -3422,7 +3519,7 @@ class MainWindow(QMainWindow):
event, time, lat, lon, depth, ml, mw, nmp, nap, tune, test, notes = row event, time, lat, lon, depth, ml, mw, nmp, nap, tune, test, notes = row
row_str = '' row_str = ''
for index in range(len(row)): for index in range(len(row)):
row_str += '{}'+'{}'.format(separator) row_str += '{}' + '{}'.format(separator)
row_str = row_str.format(event.text(), time.text(), lat.text(), lon.text(), depth.text(), ml.text(), row_str = row_str.format(event.text(), time.text(), lat.text(), lon.text(), depth.text(), ml.text(),
mw.text(), nmp.text(), nap.text(), bool(tune.checkState()), mw.text(), nmp.text(), nap.text(), bool(tune.checkState()),
@@ -3451,7 +3548,6 @@ class MainWindow(QMainWindow):
if event == current_event: if event == current_event:
set_background_color(item_list, QtGui.QColor(*(0, 143, 143, 255))) set_background_color(item_list, QtGui.QColor(*(0, 143, 143, 255)))
def set_metadata(self): def set_metadata(self):
self.project.inventories = self.metadata.inventories self.project.inventories = self.metadata.inventories
if self.metadata.inventories: if self.metadata.inventories:
@@ -3463,7 +3559,7 @@ class MainWindow(QMainWindow):
self.init_map_button.setEnabled(False) self.init_map_button.setEnabled(False)
self.initMapAction.setEnabled(False) self.initMapAction.setEnabled(False)
self.inventory_label.setText("No inventory set...") self.inventory_label.setText("No inventory set...")
#self.setDirty(False) # self.setDirty(False)
def add_metadata(self): def add_metadata(self):
self.add_metadata_widget = AddMetadataWidget(self, metadata=self.metadata) self.add_metadata_widget = AddMetadataWidget(self, metadata=self.metadata)
@@ -3484,7 +3580,7 @@ class MainWindow(QMainWindow):
wf_select = Stream() wf_select = Stream()
# restitute only picked traces # restitute only picked traces
for station in np.unique(self.getPicks('manual').keys() + self.getPicks('auto').keys()): for station in np.unique(list(self.getPicks('manual').keys()) + list(self.getPicks('auto').keys())):
wf_select += wf_copy.select(station=station) wf_select += wf_copy.select(station=station)
corr_wf = restitute_data(wf_select, self.metadata) corr_wf = restitute_data(wf_select, self.metadata)
@@ -3663,7 +3759,7 @@ class MainWindow(QMainWindow):
filename = fnm[0] + '.plp' filename = fnm[0] + '.plp'
self.project.parameter = self._inputs self.project.parameter = self._inputs
settings = QSettings() settings = QSettings()
autosaveXML = get_Bool(settings.value('autosaveXML', True)) autosaveXML = get_bool(settings.value('autosaveXML', True))
if autosaveXML: if autosaveXML:
self.exportEvents() self.exportEvents()
if not self.project.save(filename): return False if not self.project.save(filename): return False
@@ -3687,7 +3783,7 @@ class MainWindow(QMainWindow):
self.metadata.clear_inventory() self.metadata.clear_inventory()
self.project.parameter = self._inputs self.project.parameter = self._inputs
settings = QSettings() settings = QSettings()
autosaveXML = get_Bool(settings.value('autosaveXML', True)) autosaveXML = get_bool(settings.value('autosaveXML', True))
if autosaveXML: if autosaveXML:
self.exportEvents() self.exportEvents()
if not self.project.save(): return False if not self.project.save(): return False
@@ -3711,7 +3807,7 @@ class MainWindow(QMainWindow):
else: else:
self.dataPlot.setPermText(1) self.dataPlot.setPermText(1)
self.dataPlot.setPermText(0, '| Number of traces: {} | Gain: {}'.format(len(self.getPlotWidget().getPlotDict()), self.dataPlot.setPermText(0, '| Number of traces: {} | Gain: {}'.format(len(self.getPlotWidget().getPlotDict()),
self.gain)) self.gain))
def _setDirty(self): def _setDirty(self):
self.setDirty(True) self.setDirty(True)
@@ -3725,6 +3821,7 @@ class MainWindow(QMainWindow):
def closeEvent(self, event): def closeEvent(self, event):
if self.okToContinue(): if self.okToContinue():
self.logwidget.close()
event.accept() event.accept()
else: else:
event.ignore() event.ignore()
@@ -3732,7 +3829,7 @@ class MainWindow(QMainWindow):
# QMainWindow.closeEvent(self, event) # QMainWindow.closeEvent(self, event)
def setParameter(self, checked=0, show=True): def setParameter(self, checked=0, show=True):
if checked: pass # dummy argument to receive trigger signal (checked) if called by QAction if checked: pass # dummy argument to receive trigger signal (checked) if called by QAction
if not self.paraBox: if not self.paraBox:
self.paraBox = PylotParaBox(self._inputs, parent=self, windowflag=Qt.Window) self.paraBox = PylotParaBox(self._inputs, parent=self, windowflag=Qt.Window)
self.paraBox.accepted.connect(self._setDirty) self.paraBox.accepted.connect(self._setDirty)
@@ -3757,7 +3854,6 @@ class MainWindow(QMainWindow):
self.plotWaveformDataThread() self.plotWaveformDataThread()
self.refreshTabs() self.refreshTabs()
def PyLoTprefs(self): def PyLoTprefs(self):
if not self._props: if not self._props:
self._props = PropertiesDlg(self, infile=self.infile, self._props = PropertiesDlg(self, infile=self.infile,
@@ -3776,7 +3872,7 @@ class MainWindow(QMainWindow):
def helpHelp(self): def helpHelp(self):
if checkurl(): if checkurl():
form = HelpForm(self, form = HelpForm(self,
'https://ariadne.geophysik.ruhr-uni-bochum.de/trac/PyLoT/wiki') 'https://github.com/seismology-RUB/PyLoT')
else: else:
form = HelpForm(self, ':/help.html') form = HelpForm(self, ':/help.html')
form.show() form.show()
@@ -3786,6 +3882,7 @@ class Project(object):
''' '''
Pickable class containing information of a PyLoT project, like event lists and file locations. Pickable class containing information of a PyLoT project, like event lists and file locations.
''' '''
# TODO: remove rootpath # TODO: remove rootpath
def __init__(self): def __init__(self):
self.eventlist = [] self.eventlist = []
@@ -3927,18 +4024,18 @@ class Project(object):
else: else:
filename = self.location filename = self.location
table = self._table # MP: see below table = self._table # MP: see below
try: try:
outfile = open(filename, 'wb') outfile = open(filename, 'wb')
self._table = [] # MP: Workaround as long as table cannot be saved as part of project self._table = [] # MP: Workaround as long as table cannot be saved as part of project
pickle.dump(self, outfile, protocol=pickle.HIGHEST_PROTOCOL) pickle.dump(self, outfile, protocol=pickle.HIGHEST_PROTOCOL)
self.setDirty(False) self.setDirty(False)
self._table = table # MP: see above self._table = table # MP: see above
return True return True
except Exception as e: except Exception as e:
print('Could not pickle PyLoT project. Reason: {}'.format(e)) print('Could not pickle PyLoT project. Reason: {}'.format(e))
self.setDirty() self.setDirty()
self._table = table # MP: see above self._table = table # MP: see above
return False return False
@staticmethod @staticmethod
@@ -3955,13 +4052,13 @@ class Project(object):
return project return project
class getExistingDirectories(QFileDialog): class GetExistingDirectories(QFileDialog):
''' '''
File dialog with possibility to select multiple folders. File dialog with possibility to select multiple folders.
''' '''
def __init__(self, *args): def __init__(self, *args):
super(getExistingDirectories, self).__init__(*args) super(GetExistingDirectories, self).__init__(*args)
self.setOption(self.DontUseNativeDialog, True) self.setOption(self.DontUseNativeDialog, True)
self.setOption(self.ReadOnly, True) self.setOption(self.ReadOnly, True)
self.setFileMode(self.Directory) self.setFileMode(self.Directory)
@@ -3987,16 +4084,7 @@ def create_window():
return app, app_created return app, app_created
def main(args=None): def main(project_filename=None, pylot_infile=None, reset_qsettings=False):
project_filename = None
# args.project_filename = 'C:/Shared/AlpArray/alparray_data/project_alparray_test.plp'
pylot_infile = None
if args:
if args.project_filename:
project_filename = args.project_filename
if args.input_filename:
pylot_infile = args.input_filename
reset_qsettings = args.reset_qsettings
# create the Qt application # create the Qt application
pylot_app, app_created = create_window() pylot_app, app_created = create_window()
@@ -4045,4 +4133,5 @@ if __name__ == "__main__":
parser.add_argument('--reset_qsettings', default=False, action='store_true', parser.add_argument('--reset_qsettings', default=False, action='store_true',
help='reset qsettings (debug option)') help='reset qsettings (debug option)')
args = parser.parse_args() args = parser.parse_args()
sys.exit(main(args)) sys.exit(main(project_filename=args.project_filename, pylot_infile=args.input_filename,
reset_qsettings=args.reset_qsettings))
+38 -23
View File
@@ -1,36 +1,54 @@
# PyLoT # PyLoT
version: 0.2 version: 0.3
The Python picking and Localisation Tool The Python picking and Localisation Tool
This python library contains a graphical user interfaces for picking This python library contains a graphical user interfaces for picking seismic phases. This software needs [ObsPy][ObsPy]
seismic phases. This software needs [ObsPy][ObsPy] and the PySide2 Qt5 bindings for python to be installed first.
and the PySide Qt4 bindings for python to be installed first.
PILOT has originally been developed in Mathworks' MatLab. In order to PILOT has originally been developed in Mathworks' MatLab. In order to distribute PILOT without facing portability
distribute PILOT without facing portability problems, it has been decided problems, it has been decided to redevelop the software package in Python. The great work of the ObsPy group allows easy
to redevelop the software package in Python. The great work of the ObsPy handling of a bunch of seismic data and PyLoT will benefit a lot compared to the former MatLab version.
group allows easy handling of a bunch of seismic data and PyLoT will
benefit a lot compared to the former MatLab version.
The development of PyLoT is part of the joint research project MAGS2 and AlpArray. The development of PyLoT is part of the joint research project MAGS2 and AlpArray.
## Installation ## Installation
At the moment there is no automatic installation procedure available for PyLoT. At the moment there is no automatic installation procedure available for PyLoT. Best way to install is to clone the
Best way to install is to clone the repository and add the path to your Python path. repository and add the path to your Python path.
It is highly recommended to use Anaconda for a simple creation of a Python installation using either the *pylot.yml* or the *requirements.txt* file found in the PyLoT root directory. First make sure that the *conda-forge* channel is available in your Anaconda installation:
conda config --add channels conda-forge
Afterwards run (from the PyLoT main directory where the files *requirements.txt* and *pylot.yml* are located)
conda env create -f pylot.yml
or
conda create --name pylot_38 --file requirements.txt
to create a new Anaconda environment called "pylot_38".
Afterwards activate the environment by typing
conda activate pylot_38
#### Prerequisites: #### Prerequisites:
In order to run PyLoT you need to install: In order to run PyLoT you need to install:
- python 2 or 3 - Python 3
- obspy
- pyside2
- pyqtgraph
- cartopy
(the following are already dependencies of the above packages):
- scipy - scipy
- numpy - numpy
- matplotlib - matplotlib <= 3.3.x
- obspy
- pyside
#### Some handwork: #### Some handwork:
@@ -53,7 +71,8 @@ In the next step you have to copy some files to this directory:
cp path-to-pylot/inputs/pylot_global.in ~/.pylot/pylot.in cp path-to-pylot/inputs/pylot_global.in ~/.pylot/pylot.in
and some extra information on error estimates (just needed for reading old PILOT data) and the Richter magnitude scaling relation and some extra information on error estimates (just needed for reading old PILOT data) and the Richter magnitude scaling
relation
cp path-to-pylot/inputs/PILOT_TimeErrors.in path-to-pylot/inputs/richter_scaling.data ~/.pylot/ cp path-to-pylot/inputs/PILOT_TimeErrors.in path-to-pylot/inputs/richter_scaling.data ~/.pylot/
@@ -61,7 +80,6 @@ You may need to do some modifications to these files. Especially folder names sh
PyLoT has been tested on Mac OSX (10.11), Debian Linux 8 and on Windows 10. PyLoT has been tested on Mac OSX (10.11), Debian Linux 8 and on Windows 10.
## Release notes ## Release notes
#### Features: #### Features:
@@ -77,20 +95,17 @@ PyLoT has been tested on Mac OSX (10.11), Debian Linux 8 and on Windows 10.
#### Known issues: #### Known issues:
- Sometimes an error might occur when using Qt
We hope to solve these with the next release. We hope to solve these with the next release.
## Staff ## Staff
Original author(s): L. Kueperkoch, S. Wehling-Benatelli, M. Bischoff (PILOT) Original author(s): M. Rische, S. Wehling-Benatelli, L. Kueperkoch, M. Bischoff (PILOT)
Developer(s): S. Wehling-Benatelli, L. Kueperkoch, K. Olbert, M. Bischoff, Developer(s): S. Wehling-Benatelli, M. Paffrath, L. Kueperkoch, K. Olbert, M. Bischoff, C. Wollin, M. Rische, D. Arnold, K. Cökerim, S. Zimmermann
C. Wollin, M. Rische, M. Paffrath
Others: A. Bruestle, T. Meier, W. Friederich Others: A. Bruestle, T. Meier, W. Friederich
[ObsPy]: http://github.com/obspy/obspy/wiki [ObsPy]: http://github.com/obspy/obspy/wiki
September 2017 April 2022
+2 -1
View File
@@ -8,6 +8,7 @@ import datetime
import glob import glob
import os import os
import traceback import traceback
from obspy import read_events from obspy import read_events
from obspy.core.event import ResourceIdentifier from obspy.core.event import ResourceIdentifier
@@ -277,7 +278,7 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even
if not wfdat: if not wfdat:
print('Could not find station {}. STOP!'.format(station)) print('Could not find station {}. STOP!'.format(station))
return return
#wfdat = remove_underscores(wfdat) # wfdat = remove_underscores(wfdat)
# trim components for each station to avoid problems with different trace starttimes for one station # trim components for each station to avoid problems with different trace starttimes for one station
wfdat = check4gapsAndRemove(wfdat) wfdat = check4gapsAndRemove(wfdat)
wfdat = check4doubled(wfdat) wfdat = check4doubled(wfdat)
+3 -1
View File
@@ -7,4 +7,6 @@
#$ -l h_vmem=2G #$ -l h_vmem=2G
#$ -l os=*stretch #$ -l os=*stretch
python ./autoPyLoT.py -i /home/marcel/.pylot/pylot_alparray_mantle_corr_stack_0.03-0.5.in -dmt processed -c $NSLOTS conda activate pylot_38
python ./autoPyLoT.py -i /home/marcel/.pylot/pylot_janis_noisy.in -c $NSLOTS
+273 -174
View File
@@ -2,36 +2,36 @@
- [PyLoT Documentation](#pylot-documentation) - [PyLoT Documentation](#pylot-documentation)
- [PyLoT GUI](#pylot-gui) - [PyLoT GUI](#pylot-gui)
- [First start](#first-start) - [First start](#first-start)
- [Main Screen](#main-screen) - [Main Screen](#main-screen)
- [Waveform Plot](#waveform-plot) - [Waveform Plot](#waveform-plot)
- [Mouse view controls](#mouse-view-controls) - [Mouse view controls](#mouse-view-controls)
- [Buttons](#buttons) - [Buttons](#buttons)
- [Array Map](#array-map) - [Array Map](#array-map)
- [Eventlist](#eventlist) - [Eventlist](#eventlist)
- [Usage](#usage) - [Usage](#usage)
- [Projects and Events](#projects-and-events) - [Projects and Events](#projects-and-events)
- [Event folder structure](#event-folder-structure) - [Event folder structure](#event-folder-structure)
- [Loading event information from CSV file](#loading-event-information-from-csv-file) - [Loading event information from CSV file](#loading-event-information-from-csv-file)
- [Adding events to project](#adding-events-to-project) - [Adding events to project](#adding-events-to-project)
- [Saving projects](#saving-projects) - [Saving projects](#saving-projects)
- [Adding metadata](#adding-metadata) - [Adding metadata](#adding-metadata)
- [Picking](#picking) - [Picking](#picking)
- [Manual Picking](#manual-picking) - [Manual Picking](#manual-picking)
- [Picking window](#picking-window) - [Picking window](#picking-window)
- [Picking Window Settings](#picking-window-settings) - [Picking Window Settings](#picking-window-settings)
- [Filtering](#filtering) - [Filtering](#filtering)
- [Export and Import of manual picks](#export-and-import-of-manual-picks) - [Export and Import of manual picks](#export-and-import-of-manual-picks)
- [Export](#export) - [Export](#export)
- [Import](#import) - [Import](#import)
- [Automatic Picking](#automatic-picking) - [Automatic Picking](#automatic-picking)
- [Tuning](#tuning) - [Tuning](#tuning)
- [Production run of the autopicker](#production-run-of-the-autopicker) - [Production run of the autopicker](#production-run-of-the-autopicker)
- [Evaluation of automatic picks](#evaluation-of-automatic-picks) - [Evaluation of automatic picks](#evaluation-of-automatic-picks)
- [1. Jackknife check](#1-jackknife-check) - [1. Jackknife check](#1-jackknife-check)
- [2. Wadati check](#2-wadati-check) - [2. Wadati check](#2-wadati-check)
- [Comparison between automatic and manual picks](#comparison-between-automatic-and-manual-picks) - [Comparison between automatic and manual picks](#comparison-between-automatic-and-manual-picks)
- [Export and Import of automatic picks](#export-and-import-of-automatic-picks) - [Export and Import of automatic picks](#export-and-import-of-automatic-picks)
- [Location determination](#location-determination) - [Location determination](#location-determination)
- [FAQ](#faq) - [FAQ](#faq)
@@ -44,15 +44,17 @@ This section describes how to use PyLoT graphically to view waveforms and create
After opening PyLoT for the first time, the setup routine asks for the following information: After opening PyLoT for the first time, the setup routine asks for the following information:
Questions: Questions:
1. Full Name 1. Full Name
2. Authority: Enter authority/institution name 2. Authority: Enter authority/institution name
3. Format: Enter output format (*.xml, *.cnv, *.obs) 3. Format: Enter output format (*.xml, *.cnv, *.obs)
[//]: <> (TODO: explain what these things mean, where they are used) [//]: <> (TODO: explain what these things mean, where they are used)
## Main Screen ## Main Screen
After entering the [information](#first-start), PyLoTs main window is shown. It defaults to a view of the [Waveform Plot](#waveform-plot), which starts empty. After entering the [information](#first-start), PyLoTs main window is shown. It defaults to a view of
the [Waveform Plot](#waveform-plot), which starts empty.
<img src=images/gui/pylot-main-screen.png alt="Tune autopicks button" title="Tune autopicks button"> <img src=images/gui/pylot-main-screen.png alt="Tune autopicks button" title="Tune autopicks button">
@@ -61,24 +63,21 @@ Add trace data by [loading a project](#projects-and-events) or by [adding event
### Waveform Plot ### Waveform Plot
The waveform plot shows a trace list of all stations of an event. The waveform plot shows a trace list of all stations of an event.
Click on any trace to open the stations [picking window](#picking-window), where you can review automatic and manual picks. Click on any trace to open the stations [picking window](#picking-window), where you can review automatic and manual
picks.
<img src=images/gui/pylot-waveform-plot.png alt="A Waveform Plot showing traces of one event"> <img src=images/gui/pylot-waveform-plot.png alt="A Waveform Plot showing traces of one event">
Above the traces the currently displayed event can be selected. Above the traces the currently displayed event can be selected. In the bottom bar information about the trace under the
In the bottom bar information about the trace under the mouse cursor is shown. This information includes the station name (station), the absolute UTC time (T) of the point under the mouse cursor and the relative time since the first trace start in seconds (t) as well as a trace count. mouse cursor is shown. This information includes the station name (station), the absolute UTC time (T) of the point
under the mouse cursor and the relative time since the first trace start in seconds (t) as well as a trace count.
#### Mouse view controls #### Mouse view controls
Hold left mouse button and drag to pan view. Hold left mouse button and drag to pan view.
Hold right mouse button and Hold right mouse button and Direction | Result --- | --- Move the mouse up | Increase amplitude scale Move the mouse
Direction | Result down | Decrease amplitude scale Move the mouse right | Increase time scale Move the mouse left | Decrease time scale
--- | ---
Move the mouse up | Increase amplitude scale
Move the mouse down | Decrease amplitude scale
Move the mouse right | Increase time scale
Move the mouse left | Decrease time scale
Press right mouse button and click "View All" from the context menu to reset the view. Press right mouse button and click "View All" from the context menu to reset the view.
@@ -86,81 +85,100 @@ Press right mouse button and click "View All" from the context menu to reset the
[//]: <> (Hack: We need these invisible spaces to add space to the first column, otherwise ) [//]: <> (Hack: We need these invisible spaces to add space to the first column, otherwise )
Icon &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; | Description | Icon &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; | Description |
--- | --- |------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
<img src="../icons/newfile.png" alt="Create new project" width="64" height="64"> | Create a new project, for more information about projects see [Projects and Events](#projects-and-events). | <img src="../icons/newfile.png" alt="Create new project" width="64" height="64"> | Create a new project, for more information about projects see [Projects and Events](#projects-and-events). |
<img src="../icons/openproject.png" alt="Open project" width="64" height="64"> | Load a project file from disk. | <img src="../icons/openproject.png" alt="Open project" width="64" height="64"> | Load a project file from disk. |
<img src="../icons/saveproject.png" alt="Save Project" width="64" height="64"> | Save all current events into an associated project file on disk. If there is no project file currently associated, you will be asked to create a new one. | <img src="../icons/saveproject.png" alt="Save Project" width="64" height="64"> | Save all current events into an associated project file on disk. If there is no project file currently associated, you will be asked to create a new one. |
<img src="../icons/saveprojectas.png" alt="Save Project as" width="64" height="64"> | Save all current events into a new project file on disk. See [Saving projects](#saving-projects). | <img src="../icons/saveprojectas.png" alt="Save Project as" width="64" height="64"> | Save all current events into a new project file on disk. See [Saving projects](#saving-projects). |
<img src="../icons/add.png" alt="Add event data" width="64" height="64"> | Add event data by selecting directories containing waveforms. For more information see [Event folder structure](#event-folder-structure). | <img src="../icons/add.png" alt="Add event data" width="64" height="64"> | Add event data by selecting directories containing waveforms. For more information see [Event folder structure](#event-folder-structure). |
<img src="../icons/openpick.png" alt="Load event information" width="64" height="64"> | Load picks/origins from disk into the currently displayed event. If a pick already exists for a station, the one from file will overwrite the existing one. | <img src="../icons/openpick.png" alt="Load event information" width="64" height="64"> | Load picks/origins from disk into the currently displayed event. If a pick already exists for a station, the one from file will overwrite the existing one. |
<img src="../icons/openpicks.png" alt="Load information for all events" width="64" height="64"> | Load picks/origins for all events of the current project. PyLoT searches for files within the directory of the event and tries to load them for that event. For this function to work, the files containing picks/origins have to be named as described in [Event folder structure](#event-folder-structure). If a pick already exists for a station, the one from file will overwrite the existing one. | <img src="../icons/openpicks.png" alt="Load information for all events" width="64" height="64"> | Load picks/origins for all events of the current project. PyLoT searches for files within the directory of the event and tries to load them for that event. For this function to work, the files containing picks/origins have to be named as described in [Event folder structure](#event-folder-structure). If a pick already exists for a station, the one from file will overwrite the existing one. |
<img src="../icons/savepicks.png" alt="Save picks" width="64" height="64"> | Save event information such as picks and origin to file. You will be asked to select a directory in which this information should be saved. | <img src="../icons/savepicks.png" alt="Save picks" width="64" height="64"> | Save event information such as picks and origin to file. You will be asked to select a directory in which this information should be saved. |
<img src="../icons/openloc.png" alt="Load location information" width="64" height="64"> | Load location information from disk, | <img src="../icons/openloc.png" alt="Load location information" width="64" height="64"> | Load location information from disk, |
<img src="../icons/Matlab_PILOT_icon.png" alt="Load legacy information" width="64" height="64"> | Load event information from a previous, MatLab based PILOT version. | <img src="../icons/Matlab_PILOT_icon.png" alt="Load legacy information" width="64" height="64"> | Load event information from a previous, MatLab based PILOT version. |
<img src="../icons/key_Z.png" alt="Display Z" width="64" height="64"> | Display Z component of streams in waveform plot. | <img src="../icons/key_Z.png" alt="Display Z" width="64" height="64"> | Display Z component of streams in waveform plot. |
<img src="../icons/key_N.png" alt="Display N" width="64" height="64"> | Display N component of streams in waveform plot. | <img src="../icons/key_N.png" alt="Display N" width="64" height="64"> | Display N component of streams in waveform plot. |
<img src="../icons/key_E.png" alt="Display E" width="64" height="64"> | Display E component of streams in waveform plot. | <img src="../icons/key_E.png" alt="Display E" width="64" height="64"> | Display E component of streams in waveform plot. |
<img src="../icons/tune.png" alt="Tune Autopicker" width="64" height="64"> | Open the [Tune Autopicker window](#tuning). | <img src="../icons/tune.png" alt="Tune Autopicker" width="64" height="64"> | Open the [Tune Autopicker window](#tuning). |
<img src="../icons/autopylot_button.png" alt="" width="64" height="64"> | Opens a window that allows starting the autopicker for all events ([Production run of the AutoPicker](#production-run-of-the-autopicker)). | <img src="../icons/autopylot_button.png" alt="" width="64" height="64"> | Opens a window that allows starting the autopicker for all events ([Production run of the AutoPicker](#production-run-of-the-autopicker)). |
<img src="../icons/compare_button.png" alt="Comparison" width="64" height="64"> | Compare automatic and manual picks, only available if automatic and manual picks for an event exist. See [Comparison between automatic and manual picks](#comparison-between-automatic-and-manual-picks). | <img src="../icons/compare_button.png" alt="Comparison" width="64" height="64"> | Compare automatic and manual picks, only available if automatic and manual picks for an event exist. See [Comparison between automatic and manual picks](#comparison-between-automatic-and-manual-picks). |
<img src="../icons/locate_button.png" alt="Locate event" width="64" height="64"> | Run a location routine (NonLinLoc) as configured in the settings on the picks. See [Location determination](#location-determination). | <img src="../icons/locate_button.png" alt="Locate event" width="64" height="64"> | Run a location routine (NonLinLoc) as configured in the settings on the picks. See [Location determination](#location-determination). |
### Array Map ### Array Map
The array map will display a color diagram to allow a visual check of the consistency of picks across multiple stations. This works by calculating the time difference of every onset to the earliest onset. Then isolines are drawn between stations with the same time difference and the areas between isolines are colored. The array map will display a color diagram to allow a visual check of the consistency of picks across multiple stations.
The result should resemble a color gradient as the wavefront rolls over the network area. Stations where picks are earlier/later than their neighbours can be reviewed by clicking on them, which opens the [picking window](#picking-window). This works by calculating the time difference of every onset to the earliest onset. Then isolines are drawn between
stations with the same time difference and the areas between isolines are colored.
The result should resemble a color gradient as the wavefront rolls over the network area. Stations where picks are
earlier/later than their neighbours can be reviewed by clicking on them, which opens
the [picking window](#picking-window).
Above the Array Map the picks that are used to create the map can be customized. Above the Array Map the picks that are used to create the map can be customized. The phase of picks that should be used
The phase of picks that should be used can be selected, which allows checking the consistency of the P- and S-phase separately. can be selected, which allows checking the consistency of the P- and S-phase separately. Additionally the pick type can
Additionally the pick type can be set to manual, automatic or hybrid, meaning display only manual picks, automatic picks or only display automatic picks for stations where there are no manual ones. be set to manual, automatic or hybrid, meaning display only manual picks, automatic picks or only display automatic
picks for stations where there are no manual ones.
![Array Map](images/gui/arraymap-example.png "Array Map") ![Array Map](images/gui/arraymap-example.png "Array Map")
*Array Map for an event at the Northern Mid Atlantic Ridge, between North Africa and Mexico (Lat. 22.58, Lon. -45.11). The wavefront moved from west to east over the network area (Alps and Balcan region), with the earliest onsets in blue in the west.* *Array Map for an event at the Northern Mid Atlantic Ridge, between North Africa and Mexico (Lat. 22.58, Lon. -45.11).
The wavefront moved from west to east over the network area (Alps and Balcan region), with the earliest onsets in blue
in the west.*
To be able to display an array map PyLoT needs to load an inventory file, where the metadata of seismic stations is kept. For more information see [Metadata](#adding-metadata). Additionally automatic or manual picks need to exist for the current event. To be able to display an array map PyLoT needs to load an inventory file, where the metadata of seismic stations is
kept. For more information see [Metadata](#adding-metadata). Additionally automatic or manual picks need to exist for
the current event.
### Eventlist ### Eventlist
The eventlist displays event parameters. The displayed parameters are saved in the .xml file in the event folder. Events can be deleted from the project by pressing the red X in the leftmost column of the corresponding event. The eventlist displays event parameters. The displayed parameters are saved in the .xml file in the event folder. Events
can be deleted from the project by pressing the red X in the leftmost column of the corresponding event.
<img src="images/gui/eventlist.png" alt="Eventlist"> <img src="images/gui/eventlist.png" alt="Eventlist">
Column | Description | Column | Description |
--- | --- |------------|--------------------------------------------------------------------------------------------------------------------|
Event | Full path to the events folder. | Event | Full path to the events folder. |
Time | Time of event. | Time | Time of event. |
Lat | Latitude in degrees of event location. | Lat | Latitude in degrees of event location. |
Lon | Longitude in degrees of event location. | Lon | Longitude in degrees of event location. |
Depth | Depth in km of event. | Depth | Depth in km of event. |
Mag | Magnitude of event. | Mag | Magnitude of event. |
[N] MP | Number of manual picks. | [N] MP | Number of manual picks. |
[N] AP | Number of automatic picks. | [N] AP | Number of automatic picks. |
Tuning Set | Select whether this event is a Tuning event. See [Automatic Picking](#automatic-picking). | Tuning Set | Select whether this event is a Tuning event. See [Automatic Picking](#automatic-picking). |
Test Set | Select whether this event is a Test event. See [Automatic Picking](#automatic-picking). | Test Set | Select whether this event is a Test event. See [Automatic Picking](#automatic-picking). |
Notes | Free form text field for notes regarding this event. Text will be saved in the notes.txt file in the event folder. | Notes | Free form text field for notes regarding this event. Text will be saved in the notes.txt file in the event folder. |
## Usage ## Usage
### Projects and Events ### Projects and Events
PyLoT uses projects to categorize different seismic data. A project consists of one or multiple events. Events contain seismic traces from one or multiple stations. An event also contains further information, e.g. origin time, source parameters and automatic as well as manual picks. PyLoT uses projects to categorize different seismic data. A project consists of one or multiple events. Events contain
Projects are used to group events which should be analysed together. A project could contain all events from a specific region within a timeframe of interest or all recorded events of a seismological experiment. seismic traces from one or multiple stations. An event also contains further information, e.g. origin time, source
parameters and automatic as well as manual picks. Projects are used to group events which should be analysed together. A
project could contain all events from a specific region within a timeframe of interest or all recorded events of a
seismological experiment.
### Event folder structure ### Event folder structure
PyLoT expects the following folder structure for seismic data: PyLoT expects the following folder structure for seismic data:
* Every event should be in it's own folder with the following naming scheme for the folders: * Every event should be in it's own folder with the following naming scheme for the folders:
``e[id].[doy].[yy]``, where ``[id]`` is a four-digit numerical id increasing from 0001, ``[doy]`` the three digit day of year and ``[yy]`` the last two digits of the year of the event. This structure has to be created by the user of PyLoT manually. ``e[id].[doy].[yy]``, where ``[id]`` is a four-digit numerical id increasing from 0001, ``[doy]`` the three digit day
of year and ``[yy]`` the last two digits of the year of the event. This structure has to be created by the user of
PyLoT manually.
* These folders should contain the seismic data for their event as ``.mseed`` or other supported filetype * These folders should contain the seismic data for their event as ``.mseed`` or other supported filetype
* All automatic and manual picks should be in an ``.xml`` file in their event folder. PyLoT saves picks in this file. This file does not have to be added manually unless there are picks to be imported. The format used to save picks is QUAKEML. * All automatic and manual picks should be in an ``.xml`` file in their event folder. PyLoT saves picks in this file.
Picks are saved in a file with the same filename as the event folder with ``PyLoT_`` prepended. This file does not have to be added manually unless there are picks to be imported. The format used to save picks is
* The file ``notes.txt`` is used for saving analysts comments. Everything saved here will be displayed in the 'Notes' column of the eventlist. QUAKEML.
Picks are saved in a file with the same filename as the event folder with ``PyLoT_`` prepended.
* The file ``notes.txt`` is used for saving analysts comments. Everything saved here will be displayed in the 'Notes'
column of the eventlist.
### Loading event information from CSV file ### Loading event information from CSV file
Event information can be saved in a ``.csv`` file located in the rootpath. The file is made from one header line, which is followed by one or multiple data lines. Values are separated by comma, while a dot is used as a decimal separator. Event information can be saved in a ``.csv`` file located in the rootpath. The file is made from one header line, which
is followed by one or multiple data lines. Values are separated by comma, while a dot is used as a decimal separator.
This information is then shown in the table in the [Eventlist tab](#Eventlist). This information is then shown in the table in the [Eventlist tab](#Eventlist).
One example header and data line is shown below. One example header and data line is shown below.
@@ -169,50 +187,63 @@ One example header and data line is shown below.
The meaning of the header entries is: The meaning of the header entries is:
Header | description | Header | description |
--- | --- |----------------------|------------------------------------------------------------------------------------------------|
event | Event id, has to be the same as the folder name in which waveform data for this event is kept. | event | Event id, has to be the same as the folder name in which waveform data for this event is kept. |
Data | Origin date of the event, format DD/MM/YY or DD/MM/YYYY. | Data | Origin date of the event, format DD/MM/YY or DD/MM/YYYY. |
Time | Origin time of the event. Format HH:MM:SS. | Time | Origin time of the event. Format HH:MM:SS. |
Lat, Long | Origin latitude and longitude in decimal degrees. | Lat, Long | Origin latitude and longitude in decimal degrees. |
Region | Flinn-Engdahl region name. | Region | Flinn-Engdahl region name. |
Basis Lat, Basis Lon | Latitude and longitude of the basis of the station network in decimal degrees. | Basis Lat, Basis Lon | Latitude and longitude of the basis of the station network in decimal degrees. |
Distance [km] | Distance from origin coordinates to basis coordinates in km. | Distance [km] | Distance from origin coordinates to basis coordinates in km. |
Distance [rad] | Distance from origin coordinates to basis coordinates in rad. | Distance [rad] | Distance from origin coordinates to basis coordinates in rad. |
### Adding events to project ### Adding events to project
PyLoT GUI starts with an empty project. To add events, use the add event data button. Select one or multiple folders containing events. PyLoT GUI starts with an empty project. To add events, use the add event data button. Select one or multiple folders
containing events.
[//]: <> (TODO: explain _Directories: Root path, Data path, Database path_) [//]: <> (TODO: explain _Directories: Root path, Data path, Database path_)
### Saving projects ### Saving projects
Save the current project from the menu with File->Save project or File->Save project as. Save the current project from the menu with File->Save project or File->Save project as. PyLoT uses ``.plp`` files to
PyLoT uses ``.plp`` files to save project information. This file format is not interchangeable between different versions of Python interpreters. save project information. This file format is not interchangeable between different versions of Python interpreters.
Saved projects contain the automatic and manual picks. Seismic trace data is not included into the ``.plp`` file, but read from its location used when saving the file. Saved projects contain the automatic and manual picks. Seismic trace data is not included into the ``.plp`` file, but
read from its location used when saving the file.
### Adding metadata ### Adding metadata
[//]: <> (TODO: Add picture of metadata "manager" when it is done) [//]: <> (TODO: Add picture of metadata "manager" when it is done)
PyLoT can handle ``.dless``, ``.xml``, ``.resp`` and ``.dseed`` file formats for Metadata. Metadata files stored on disk can be added to a project by clicking *Edit*->*Manage Inventories*. This opens up a window where the folders which contain metadata files can be selected. PyLoT will then search these files for the station names when it needs the information. PyLoT can handle ``.dless``, ``.xml``, ``.resp`` and ``.dseed`` file formats for Metadata. Metadata files stored on disk
can be added to a project by clicking *Edit*->*Manage Inventories*. This opens up a window where the folders which
contain metadata files can be selected. PyLoT will then search these files for the station names when it needs the
information.
# Picking # Picking
PyLoTs automatic and manual pick determination works as following: PyLoTs automatic and manual pick determination works as following:
* Using certain parameters, a first initial/coarse pick is determined. The first manual pick is determined by visual review of the whole waveform and selection of the most likely onset by the analyst. The first automatic pick is determined by calculation of a characteristic function (CF) for the seismic trace. When a wave arrives, the CFs properties change, which is determined as the signals onset.
* Afterwards, a refined set of parameters is applied to a small part of the waveform around the initial onset. For manual picks this means a closer view of the trace, for automatic picks this is done by a recalculated CF with different parameters. * Using certain parameters, a first initial/coarse pick is determined. The first manual pick is determined by visual
review of the whole waveform and selection of the most likely onset by the analyst. The first automatic pick is
determined by calculation of a characteristic function (CF) for the seismic trace. When a wave arrives, the CFs
properties change, which is determined as the signals onset.
* Afterwards, a refined set of parameters is applied to a small part of the waveform around the initial onset. For
manual picks this means a closer view of the trace, for automatic picks this is done by a recalculated CF with
different parameters.
* This second picking phase results in the precise pick, which is treated as the onset time. * This second picking phase results in the precise pick, which is treated as the onset time.
## Manual Picking ## Manual Picking
To create manual picks, you will need to open or create a project that contains seismic trace data (see [Adding events to projects](#adding-events-to-project)). Click on a trace to open the [Picking window](#picking-window). To create manual picks, you will need to open or create a project that contains seismic trace data (
see [Adding events to projects](#adding-events-to-project)). Click on a trace to open
the [Picking window](#picking-window).
### Picking window ### Picking window
Open the picking window of a station by leftclicking on any trace in the waveform plot. Here you can create manual picks for the selected station. Open the picking window of a station by leftclicking on any trace in the waveform plot. Here you can create manual picks
for the selected station.
<img src="images/gui/picking/pickwindow.png" alt="Picking window"> <img src="images/gui/picking/pickwindow.png" alt="Picking window">
@@ -220,24 +251,24 @@ Open the picking window of a station by leftclicking on any trace in the wavefor
#### Picking Window Settings #### Picking Window Settings
Icon | Shortcut | Menu Alternative | Description | Icon | Shortcut | Menu Alternative | Description |
---|---|---|--- |----------------------------------------------------------------------------------|----------------|-----------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
<img src="../icons/filter_p.png" alt="Filter P" width="64" height="64"> | p | Filter->Apply P Filter | Filter all channels according to the options specified in Filter parameter, P Filter section. | <img src="../icons/filter_p.png" alt="Filter P" width="64" height="64"> | p | Filter->Apply P Filter | Filter all channels according to the options specified in Filter parameter, P Filter section. |
<img src="../icons/filter_s.png" alt="Filter S" width="64" height="64"> | s | Filter->Apply S Filter | Filter all channels according to the options specified in Filter parameter, S Filter section. | <img src="../icons/filter_s.png" alt="Filter S" width="64" height="64"> | s | Filter->Apply S Filter | Filter all channels according to the options specified in Filter parameter, S Filter section. |
<img src="../icons/key_A.png" alt="Filter Automatically" width="64" height="64"> | Ctrl + a | Filter->Automatic Filtering | If enabled, automatically select the correct filter option (P, S) depending on the selected phase to be picked. | <img src="../icons/key_A.png" alt="Filter Automatically" width="64" height="64"> | Ctrl + a | Filter->Automatic Filtering | If enabled, automatically select the correct filter option (P, S) depending on the selected phase to be picked. |
![desc](images/gui/picking/phase_selection.png "Phase selection") | 1 (P) or 5 (S) | Picks->P or S | Select phase to pick. If Automatic Filtering is enabled, this will apply the appropriate filter depending on the phase. | ![desc](images/gui/picking/phase_selection.png "Phase selection") | 1 (P) or 5 (S) | Picks->P or S | Select phase to pick. If Automatic Filtering is enabled, this will apply the appropriate filter depending on the phase. |
![Zoom into](../icons/zoom_in.png "Zoom into waveform") | - | - | Zoom into waveform. | ![Zoom into](../icons/zoom_in.png "Zoom into waveform") | - | - | Zoom into waveform. |
![Reset zoom](../icons/zoom_0.png "Reset zoom") | - | - | Reset zoom to default view. | ![Reset zoom](../icons/zoom_0.png "Reset zoom") | - | - | Reset zoom to default view. |
![Delete picks](../icons/delete.png "Delete picks") | - | - | Delete all manual picks on this station. | ![Delete picks](../icons/delete.png "Delete picks") | - | - | Delete all manual picks on this station. |
![Rename a phase](../icons/sync.png "Rename a phase") | - | - | Click this button and then the picked phase to rename it. | ![Rename a phase](../icons/sync.png "Rename a phase") | - | - | Click this button and then the picked phase to rename it. |
![Continue](images/gui/picking/continue.png "Continue with next station") | - | - | If checked, after accepting the manual picks for this station with 'OK', the picking window for the next station will be opened. This option is useful for fast manual picking of a complete event. | ![Continue](images/gui/picking/continue.png "Continue with next station") | - | - | If checked, after accepting the manual picks for this station with 'OK', the picking window for the next station will be opened. This option is useful for fast manual picking of a complete event. |
Estimated onsets | - | - | Show the theoretical onsets for this station. Needs metadata and origin information. | Estimated onsets | - | - | Show the theoretical onsets for this station. Needs metadata and origin information. |
Compare to channel | - | - | Select a data channel to compare against. The selected channel will be displayed in the picking window behind every channel allowing the analyst to visually compare signal correlation between different channels. | Compare to channel | - | - | Select a data channel to compare against. The selected channel will be displayed in the picking window behind every channel allowing the analyst to visually compare signal correlation between different channels. |
Scaling | - | - | Individual means every channel is scaled to its own maximum. If a channel is selected here, all channels will be scaled relatively to this channel. | Scaling | - | - | Individual means every channel is scaled to its own maximum. If a channel is selected here, all channels will be scaled relatively to this channel. |
Menu Command | Shortcut | Description | Menu Command | Shortcut | Description |
---|---|--- |---------------------------|----------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
P Channels and S Channels | - | Select which channels should be treated as P or S channels during picking. When picking a phase, only the corresponding channels will be shown during the precise pick. Normally, the Z channel should be selected for the P phase and the N and E channel for the S phase. | P Channels and S Channels | - | Select which channels should be treated as P or S channels during picking. When picking a phase, only the corresponding channels will be shown during the precise pick. Normally, the Z channel should be selected for the P phase and the N and E channel for the S phase. |
### Filtering ### Filtering
@@ -245,105 +276,167 @@ Access the Filter options by pressing Ctrl+f on the Waveform plot or by the menu
<img src=images/gui/pylot-filter-options.png> <img src=images/gui/pylot-filter-options.png>
Here you are able to select filter type, order and frequencies for the P and S pick separately. These settings are used in the GUI for displaying the filtered waveform data and during manual picking. The values used by PyLoT for automatic picking are displayed next to the manual values. They can be changed in the [Tune Autopicker dialog](#tuning). Here you are able to select filter type, order and frequencies for the P and S pick separately. These settings are used
A green value automatic value means the automatic and manual filter parameter is configured the same, red means they are configured differently. in the GUI for displaying the filtered waveform data and during manual picking. The values used by PyLoT for automatic
By toggling the "Overwrite filteroptions" checkmark you can set whether the manual precise/second pick uses the filter settings for the automatic picker (unchecked) or whether it uses the filter options in this dialog (checked). picking are displayed next to the manual values. They can be changed in the [Tune Autopicker dialog](#tuning).
To guarantee consistent picking results between automatic and manual picking it is recommended to use the same filter settings for the determination of automatic and manual picks. A green value automatic value means the automatic and manual filter parameter is configured the same, red means they are
configured differently. By toggling the "Overwrite filteroptions" checkmark you can set whether the manual
precise/second pick uses the filter settings for the automatic picker (unchecked) or whether it uses the filter options
in this dialog (checked). To guarantee consistent picking results between automatic and manual picking it is recommended
to use the same filter settings for the determination of automatic and manual picks.
### Export and Import of manual picks ### Export and Import of manual picks
#### Export #### Export
After the creation of manual picks they can either be saved in the project file (see [Saving projects](#saving-projects)). Alternatively the picks can be exported by pressing the <img src="../icons/savepicks.png" alt="Save event information button" title="Save picks button" height=24 width=24> button above the waveform plot or in the menu File->Save event information (shortcut Ctrl+p). Select the event directory in which to save the file. The filename will be ``PyLoT_[event_folder_name].[filetype selected during first startup]``. After the creation of manual picks they can either be saved in the project file (
You can rename and copy this file, but PyLoT will then no longer be able to automatically recognize the correct picks for an event and the file will have to be manually selected when loading. see [Saving projects](#saving-projects)). Alternatively the picks can be exported by pressing
the <img src="../icons/savepicks.png" alt="Save event information button" title="Save picks button" height=24 width=24>
button above the waveform plot or in the menu File->Save event information (shortcut Ctrl+p). Select the event directory
in which to save the file. The filename will be ``PyLoT_[event_folder_name].[filetype selected during first startup]``
.
You can rename and copy this file, but PyLoT will then no longer be able to automatically recognize the correct picks
for an event and the file will have to be manually selected when loading.
#### Import #### Import
To import previously saved picks press the <img src="../icons/openpick.png" alt="Load event information button" width="24" height="24"> button and select the file to load. You will be asked to save the current state of your project if you have not done so before. You can continue without saving by pressing "Discard". This does not delete any information from your project, it just means that no project file is saved before the changes of importing picks are applied. To import previously saved picks press
PyLoT will automatically load files named after the scheme it uses when saving picks, described in the paragraph above. If it can't find any matching files, a file dialogue will open and you can select the file you wish to load. the <img src="../icons/openpick.png" alt="Load event information button" width="24" height="24"> button and select the
file to load. You will be asked to save the current state of your project if you have not done so before. You can
continue without saving by pressing "Discard". This does not delete any information from your project, it just means
that no project file is saved before the changes of importing picks are applied. PyLoT will automatically load files
named after the scheme it uses when saving picks, described in the paragraph above. If it can't find any matching files,
a file dialogue will open and you can select the file you wish to load.
If you see a warning "Mismatch in event identifiers" and are asked whether to continue loading the picks, this means that PyLoT doesn't recognize the picks in the file as belonging to this specific event. They could have either been saved under a different installation of PyLoT but with the same waveform data, which means they are still compatible and you can continue loading them. Or they could be picks from a different event, in which case loading them is not recommended. If you see a warning "Mismatch in event identifiers" and are asked whether to continue loading the picks, this means
that PyLoT doesn't recognize the picks in the file as belonging to this specific event. They could have either been
saved under a different installation of PyLoT but with the same waveform data, which means they are still compatible and
you can continue loading them. Or they could be picks from a different event, in which case loading them is not
recommended.
## Automatic Picking ## Automatic Picking
The general workflow for automatic picking is as following: The general workflow for automatic picking is as following:
- After setting up the project by loading waveforms and optionally metadata, the right parameters for the autopicker have to be determined
- After setting up the project by loading waveforms and optionally metadata, the right parameters for the autopicker
have to be determined
- This [tuning](#tuning) is done for single stations with immediate graphical feedback of all picking results - This [tuning](#tuning) is done for single stations with immediate graphical feedback of all picking results
- Afterwards the autopicker can be run for all or a subset of events from the project - Afterwards the autopicker can be run for all or a subset of events from the project
For automatic picking PyLoT discerns between tune and test events, which the user has to set as such. Tune events are used to calibrate the autopicking algorithm, test events are then used to test the calibration. The purpose of that is controlling whether the parameters found during tuning are able to reliably pick the "unknown" test events. For automatic picking PyLoT discerns between tune and test events, which the user has to set as such. Tune events are
If this behaviour is not desired and all events should be handled the same, dont mark any events. Since this is just a way to group events to compare the picking results, nothing else will change. used to calibrate the autopicking algorithm, test events are then used to test the calibration. The purpose of that is
controlling whether the parameters found during tuning are able to reliably pick the "unknown" test events.
If this behaviour is not desired and all events should be handled the same, dont mark any events. Since this is just a
way to group events to compare the picking results, nothing else will change.
### Tuning ### Tuning
Tuning describes the process of adjusting the autopicker settings to the characteristics of your data set. To do this in PyLoT, use the <img src=../icons/tune.png height=24 alt="Tune autopicks button" title="Tune autopicks button"> button to open the Tune Autopicker. Tuning describes the process of adjusting the autopicker settings to the characteristics of your data set. To do this in
PyLoT, use the <img src=../icons/tune.png height=24 alt="Tune autopicks button" title="Tune autopicks button"> button to
open the Tune Autopicker.
<img src=images/gui/tuning/tune_autopicker.png> <img src=images/gui/tuning/tune_autopicker.png>
View of a station in the Tune Autopicker window. View of a station in the Tune Autopicker window.
1. Select the event to be displayed and processed. 1. Select the event to be displayed and processed.
2. Select the station from the event. 2. Select the station from the event.
3. To pick the currently displayed trace, click the <img src=images/gui/tuning/autopick_trace_button.png alt="Pick trace button" title="Autopick trace button" height=16> button. 3. To pick the currently displayed trace, click
4. These tabs are used to select the current view. __Traces Plot__ contains a plot of the stations traces, where manual picks can be created/edited. __Overview__ contains graphical results of the automatic picking process. The __P and S tabs__ contain the automatic picking results of the P and S phase, while __log__ contains a useful text output of automatic picking. the <img src=images/gui/tuning/autopick_trace_button.png alt="Pick trace button" title="Autopick trace button" height=16>
5. These buttons are used to load/save/reset settings for automatic picking. The parameters can be saved in PyLoT input files, which have the file ending *.in*. They are human readable text files, which can also be edited by hand. Saving the parameters allows you to load them again later, even on different machines. button.
6. These menus control the behaviour of the creation of manual picks from the Tune Autopicker window. Picks allows to select the phase for which a manual pick should be created, Filter allows to filter waveforms and edit the filter parameters. P-Channels and S-Channels allow to select the channels that should be displayed when creating a manual P or S pick. 4. These tabs are used to select the current view. __Traces Plot__ contains a plot of the stations traces, where manual
7. This menu is the same as in the [Picking Window](#picking-window-settings), with the exception of the __Manual Onsets__ options. The __Manual Onsets__ buttons accepts or reject the manual picks created in the Tune Autopicker window, pressing accept adds them to the manual picks for the event, while reject removes them. picks can be created/edited. __Overview__ contains graphical results of the automatic picking process. The __P and S
tabs__ contain the automatic picking results of the P and S phase, while __log__ contains a useful text output of
automatic picking.
5. These buttons are used to load/save/reset settings for automatic picking. The parameters can be saved in PyLoT input
files, which have the file ending *.in*. They are human readable text files, which can also be edited by hand. Saving
the parameters allows you to load them again later, even on different machines.
6. These menus control the behaviour of the creation of manual picks from the Tune Autopicker window. Picks allows to
select the phase for which a manual pick should be created, Filter allows to filter waveforms and edit the filter
parameters. P-Channels and S-Channels allow to select the channels that should be displayed when creating a manual P
or S pick.
7. This menu is the same as in the [Picking Window](#picking-window-settings), with the exception of the __Manual
Onsets__ options. The __Manual Onsets__ buttons accepts or reject the manual picks created in the Tune Autopicker
window, pressing accept adds them to the manual picks for the event, while reject removes them.
8. The traces plot in the centre allows creating manual picks and viewing the waveforms. 8. The traces plot in the centre allows creating manual picks and viewing the waveforms.
9. The parameters which influence the autopicking result are in the Main settings and Advanced settings tabs on the left side. For a description of all the parameters see the [tuning documentation](tuning.md). 9. The parameters which influence the autopicking result are in the Main settings and Advanced settings tabs on the left
side. For a description of all the parameters see the [tuning documentation](tuning.md).
### Production run of the autopicker ### Production run of the autopicker
After the settings used during tuning give the desired results, the autopicker can be used on the complete dataset. To invoke the autopicker on the whole set of events, click the <img src=../icons/autopylot_button.png alt="Autopick" title="Autopick" height=32> button. After the settings used during tuning give the desired results, the autopicker can be used on the complete dataset. To
invoke the autopicker on the whole set of events, click
the <img src=../icons/autopylot_button.png alt="Autopick" title="Autopick" height=32> button.
### Evaluation of automatic picks ### Evaluation of automatic picks
PyLoT has two internal consistency checks for automatic picks that were determined for an event: PyLoT has two internal consistency checks for automatic picks that were determined for an event:
1. Jackknife check 1. Jackknife check
2. Wadati check 2. Wadati check
#### 1. Jackknife check #### 1. Jackknife check
The jackknife test in PyLoT checks the consistency of automatically determined P-picks by checking the statistical variance of the picks. The variance of all P-picks is calculated and compared to the variance of subsets, in which one pick is removed. The jackknife test in PyLoT checks the consistency of automatically determined P-picks by checking the statistical
The idea is, that picks that are close together in time should not influence the estimation of the variance much, while picks whose positions deviates from the norm influence the variance to a greater extent. If the estimated variance of a subset with a pick removed differs to much from the estimated variance of all picks, the pick that was removed from the subset will be marked as invalid. variance of the picks. The variance of all P-picks is calculated and compared to the variance of subsets, in which one
The factor by which picks are allowed to skew from the estimation of variance can be configured, it is called *jackfactor*, see [here](tuning.md#Pick-quality-control). pick is removed.
The idea is, that picks that are close together in time should not influence the estimation of the variance much, while
picks whose positions deviates from the norm influence the variance to a greater extent. If the estimated variance of a
subset with a pick removed differs to much from the estimated variance of all picks, the pick that was removed from the
subset will be marked as invalid.
The factor by which picks are allowed to skew from the estimation of variance can be configured, it is called *
jackfactor*, see [here](tuning.md#Pick-quality-control).
Additionally, the deviation of picks from the median is checked. For that, the median of all P-picks that passed the Jackknife test is calculated. Picks whose onset times deviate from the mean onset time by more than the *mdttolerance* are marked as invalid. Additionally, the deviation of picks from the median is checked. For that, the median of all P-picks that passed the
Jackknife test is calculated. Picks whose onset times deviate from the mean onset time by more than the *mdttolerance*
are marked as invalid.
<img src=images/gui/jackknife_plot.png title="Jackknife/Median test diagram"> <img src=images/gui/jackknife_plot.png title="Jackknife/Median test diagram">
*The result of both tests (Jackknife and Median) is shown in a diagram afterwards. The onset time is plotted against a running number of stations. Picks that failed either the Jackknife or the median test are colored red. The median is plotted as a green line.* *The result of both tests (Jackknife and Median) is shown in a diagram afterwards. The onset time is plotted against a
running number of stations. Picks that failed either the Jackknife or the median test are colored red. The median is
plotted as a green line.*
The Jackknife and median check are suitable to check for picks that are outside of the expected time window, for example, when a wrong phase was picked. It won't recognize picks that are in close proximity to the right onset which are just slightly to late/early. The Jackknife and median check are suitable to check for picks that are outside of the expected time window, for
example, when a wrong phase was picked. It won't recognize picks that are in close proximity to the right onset which
are just slightly to late/early.
#### 2. Wadati check #### 2. Wadati check
The Wadati check checks the consistency of S picks. For this the SP-time, the time difference between S and P onset is plotted against the P onset time. A line is fitted to the points, which minimizes the error. Then the deviation of single picks to this line is checked. If the deviation in seconds is above the *wdttolerance* parameter ([see here](tuning.md#Pick-quality-control)), the pick is marked as invalid. The Wadati check checks the consistency of S picks. For this the SP-time, the time difference between S and P onset is
plotted against the P onset time. A line is fitted to the points, which minimizes the error. Then the deviation of
single picks to this line is checked. If the deviation in seconds is above the *wdttolerance*
parameter ([see here](tuning.md#Pick-quality-control)), the pick is marked as invalid.
<img src=images/gui/wadati_plot.png title="Output diagram of Wadati check"> <img src=images/gui/wadati_plot.png title="Output diagram of Wadati check">
*The Wadati plot in PyLoT shows the SP onset time difference over the P onset time. A first line is fitted (black). All picks which deviate to much from this line are marked invalid (red). Then a second line is fitted which excludes the invalid picks. From this lines slope, the ratio of P and S wave velocity is determined.* *The Wadati plot in PyLoT shows the SP onset time difference over the P onset time. A first line is fitted (black). All
picks which deviate to much from this line are marked invalid (red). Then a second line is fitted which excludes the
invalid picks. From this lines slope, the ratio of P and S wave velocity is determined.*
### Comparison between automatic and manual picks ### Comparison between automatic and manual picks
Every pick in PyLoT consists of an earliest possible, latest possible and most likely onset time. Every pick in PyLoT consists of an earliest possible, latest possible and most likely onset time. The earliest and
The earliest and latest possible onset time characterize the uncertainty of a pick. latest possible onset time characterize the uncertainty of a pick. This approach is described in Diel, Kissling and
This approach is described in Diel, Kissling and Bormann (2012) - Tutorial for consistent phase picking at local to regional distances. Bormann (2012) - Tutorial for consistent phase picking at local to regional distances. These times are represented as a
These times are represented as a Probability Density Function (PDF) for every pick. Probability Density Function (PDF) for every pick. The PDF is implemented as two exponential distributions around the
The PDF is implemented as two exponential distributions around the most likely onset as the expected value. most likely onset as the expected value.
To compare two single picks, their PDFs are cross correlated to create a new PDF. To compare two single picks, their PDFs are cross correlated to create a new PDF. This corresponds to the subtraction of
This corresponds to the subtraction of the automatic pick from the manual pick. the automatic pick from the manual pick.
<img src=images/gui/comparison/comparison_pdf.png title="Comparison between automatic and manual pick"> <img src=images/gui/comparison/comparison_pdf.png title="Comparison between automatic and manual pick">
*Comparison between an automatic and a manual pick for a station in PyLoT by comparing their PDFs.* *Comparison between an automatic and a manual pick for a station in PyLoT by comparing their PDFs.*
*The upper plot shows the difference between the two single picks that are shown in the lower plot.* *The upper plot shows the difference between the two single picks that are shown in the lower plot.*
*The difference is implemented as a cross correlation between the two PDFs. and results in a new PDF, the comparison PDF.* *The difference is implemented as a cross correlation between the two PDFs. and results in a new PDF, the comparison
*The expected value of the comparison PDF corresponds to the time distance between the automatic and manual picks most likely onset.* PDF.*
*The standard deviation corresponds to the combined uncertainty.* *The expected value of the comparison PDF corresponds to the time distance between the automatic and manual picks most
likely onset.*
*The standard deviation corresponds to the combined uncertainty.*
To compare the automatic and manual picks between multiple stations of an event, the properties of all the comparison PDFs are shown in a histogram. To compare the automatic and manual picks between multiple stations of an event, the properties of all the comparison
PDFs are shown in a histogram.
<img src=images/gui/comparison/compare_widget.png title="Comparison between picks of an event"> <img src=images/gui/comparison/compare_widget.png title="Comparison between picks of an event">
@@ -352,11 +445,13 @@ To compare the automatic and manual picks between multiple stations of an event,
*The bottom left plot shows the expected values of the comparison PDFs for P picks.* *The bottom left plot shows the expected values of the comparison PDFs for P picks.*
*The top right plot shows the standard deviation of the comparison PDFs for S picks.* *The top right plot shows the standard deviation of the comparison PDFs for S picks.*
*The bottom right plot shows the expected values of the comparison PDFs for S picks.* *The bottom right plot shows the expected values of the comparison PDFs for S picks.*
*The standard deviation plots show that most P picks have an uncertainty between 1 and 2 seconds, while S pick uncertainties have a much larger spread between 1 to 15 seconds.* *The standard deviation plots show that most P picks have an uncertainty between 1 and 2 seconds, while S pick
uncertainties have a much larger spread between 1 to 15 seconds.*
*This means P picks have higher quality classes on average than S picks.* *This means P picks have higher quality classes on average than S picks.*
*The expected values are largely negative, meaning that the algorithm tends to pick earlier than the analyst with the applied settings (Manual - Automatic).* *The expected values are largely negative, meaning that the algorithm tends to pick earlier than the analyst with the
*The number of samples mentioned in the plots legends is the amount of stations that have an automatic and a manual P pick.* applied settings (Manual - Automatic).*
*The number of samples mentioned in the plots legends is the amount of stations that have an automatic and a manual P
pick.*
### Export and Import of automatic picks ### Export and Import of automatic picks
@@ -369,7 +464,11 @@ To be added.
# FAQ # FAQ
Q: During manual picking the error "No channel to plot for phase ..." is displayed, and I am unable to create a pick. Q: During manual picking the error "No channel to plot for phase ..." is displayed, and I am unable to create a pick.
A: Select a channel that should be used for the corresponding phase in the Pickwindow. For further information read [Picking Window settings](#picking-window-settings). A: Select a channel that should be used for the corresponding phase in the Pickwindow. For further information
read [Picking Window settings](#picking-window-settings).
Q: I see a warning "Mismatch in event identifiers" when loading picks from a file. Q: I see a warning "Mismatch in event identifiers" when loading picks from a file.
A: This means that PyLoT doesn't recognize the picks in the file as belonging to this specific event. They could have been saved under a different installation of PyLoT but with the same waveform data, which means they are still compatible and you can continue loading them or they could be the picks of a different event, in which case loading them is not recommended. A: This means that PyLoT doesn't recognize the picks in the file as belonging to this specific event. They could have
been saved under a different installation of PyLoT but with the same waveform data, which means they are still
compatible and you can continue loading them or they could be the picks of a different event, in which case loading them
is not recommended.
+111 -61
View File
@@ -6,95 +6,145 @@ A description of the parameters used for determining automatic picks.
Parameters applied to the traces before picking algorithm starts. Parameters applied to the traces before picking algorithm starts.
Name | Description | Name | Description |
--- | --- |---------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
*P Start*, *P Stop* | Define time interval relative to trace start time for CF calculation on vertical trace. Value is relative to theoretical onset time if 'Use TauPy' option is enabled in main settings of 'Tune Autopicker' dialogue. | *P Start*, *P
*S Start*, *S Stop* | Define time interval relative to trace start time for CF calculation on horizontal traces. Value is relative to theoretical onset time if 'Use TauPy' option is enabled in main settings of 'Tune Autopicker' dialogue. Stop* | Define time interval relative to trace start time for CF calculation on vertical trace. Value is relative to theoretical onset time if 'Use TauPy' option is enabled in main settings of 'Tune Autopicker' dialogue. |
*Bandpass Z1* | Filter settings for Butterworth bandpass applied to vertical trace for calculation of initial P pick. | *S Start*, *S
*Bandpass Z2* | Filter settings for Butterworth bandpass applied to vertical trace for calculation of precise P pick. Stop* | Define time interval relative to trace start time for CF calculation on horizontal traces. Value is relative to theoretical onset time if 'Use TauPy' option is enabled in main settings of 'Tune Autopicker' dialogue. |
*Bandpass H1* | Filter settings for Butterworth bandpass applied to horizontal traces for calculation of initial S pick. | *Bandpass
*Bandpass H2* | Filter settings for Butterworth bandpass applied to horizontal traces for calculation of precise S pick. Z1* | Filter settings for Butterworth bandpass applied to vertical trace for calculation of initial P pick. |
| *Bandpass
Z2* | Filter settings for Butterworth bandpass applied to vertical trace for calculation of precise P pick. |
| *Bandpass
H1* | Filter settings for Butterworth bandpass applied to horizontal traces for calculation of initial S pick. |
| *Bandpass
H2* | Filter settings for Butterworth bandpass applied to horizontal traces for calculation of precise S pick. |
## Inital P pick ## Inital P pick
Parameters used for determination of initial P pick. Parameters used for determination of initial P pick.
Name | Description | Name | Description |
--- | --- |--------------|------------------------------------------------------------------------------------------------------------------------------|
*tLTA* | Size of gliding LTA window in seconds used for calculation of HOS-CF. | *
*pickwin P* | Size of time window in seconds in which the minimum of the AIC-CF in front of the maximum of the HOS-CF is determined. tLTA* | Size of gliding LTA window in seconds used for calculation of HOS-CF. |
*AICtsmooth* | Average of samples in this time window will be used for smoothing of the AIC-CF. | *pickwin
*checkwinP* | Time in front of the global maximum of the HOS-CF in which to search for a second local extrema. P* | Size of time window in seconds in which the minimum of the AIC-CF in front of the maximum of the HOS-CF is determined. |
*minfactorP* | Used with *checkwinP*. If a second local maximum is found, it has to be at least as big as the first maximum * *minfactorP*. | *
*tsignal* | Time window in seconds after the initial P pick used for determining signal amplitude. AICtsmooth* | Average of samples in this time window will be used for smoothing of the AIC-CF. |
*tnoise* | Time window in seconds in front of initial P pick used for determining noise amplitude. | *
*tsafetey* | Time in seconds between *tsignal* and *tnoise*. checkwinP* | Time in front of the global maximum of the HOS-CF in which to search for a second local extrema. |
*tslope* | Time window in seconds after initial P pick in which the slope of the onset is calculated. | *minfactorP* | Used with *
checkwinP*. If a second local maximum is found, it has to be at least as big as the first maximum * *minfactorP*. |
| *
tsignal* | Time window in seconds after the initial P pick used for determining signal amplitude. |
| *
tnoise* | Time window in seconds in front of initial P pick used for determining noise amplitude. |
| *tsafetey* | Time in seconds between *tsignal* and *
tnoise*. |
| *
tslope* | Time window in seconds after initial P pick in which the slope of the onset is calculated. |
## Inital S pick ## Inital S pick
Parameters used for determination of initial S pick Parameters used for determination of initial S pick
Name | Description | Name | Description |
--- | --- |---------------|------------------------------------------------------------------------------------------------------------------------------|
*tdet1h* | Length of time window in seconds in which AR params of the waveform are determined. | *
*tpred1h* | Length of time window in seconds in which the waveform is predicted using the AR model. tdet1h* | Length of time window in seconds in which AR params of the waveform are determined. |
*AICtsmoothS* | Average of samples in this time window is used for smoothing the AIC-CF. | *
*pickwinS* | Time window in which the minimum in the AIC-CF in front of the maximum in the ARH-CF is determined. tpred1h* | Length of time window in seconds in which the waveform is predicted using the AR model. |
*checkwinS* | Time in front of the global maximum of the ARH-CF in which to search for a second local extrema. | *
*minfactorP* | Used with *checkwinS*. If a second local maximum is found, it has to be at least as big as the first maximum * *minfactorS*. AICtsmoothS* | Average of samples in this time window is used for smoothing the AIC-CF. |
*tsignal* | Time window in seconds after the initial P pick used for determining signal amplitude. | *
*tnoise* | Time window in seconds in front of initial P pick used for determining noise amplitude. pickwinS* | Time window in which the minimum in the AIC-CF in front of the maximum in the ARH-CF is determined. |
*tsafetey* | Time in seconds between *tsignal* and *tnoise*. | *
*tslope* | Time window in seconds after initial P pick in which the slope of the onset is calculated. checkwinS* | Time in front of the global maximum of the ARH-CF in which to search for a second local extrema. |
| *minfactorP* | Used with *
checkwinS*. If a second local maximum is found, it has to be at least as big as the first maximum * *minfactorS*. |
| *
tsignal* | Time window in seconds after the initial P pick used for determining signal amplitude. |
| *
tnoise* | Time window in seconds in front of initial P pick used for determining noise amplitude. |
| *tsafetey* | Time in seconds between *tsignal* and *
tnoise*. |
| *
tslope* | Time window in seconds after initial P pick in which the slope of the onset is calculated. |
## Precise P pick ## Precise P pick
Parameters used for determination of precise P pick. Parameters used for determination of precise P pick.
Name | Description | Name | Description |
--- | --- |--------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
*Precalcwin* | Time window in seconds for recalculation of the HOS-CF. The new CF will be two times the size of *Precalcwin*, since it will be calculated from the initial pick to +/- *Precalcwin*. | *Precalcwin* | Time window in seconds for recalculation of the HOS-CF. The new CF will be two times the size of *
*tsmoothP* | Average of samples in this time window will be used for smoothing the second HOS-CF. Precalcwin*, since it will be calculated from the initial pick to +/- *Precalcwin*. |
*ausP* | Controls artificial uplift of samples during precise picking. A common local minimum of the smoothed and unsmoothed HOS-CF is found when the previous sample is larger or equal to the current sample times (1+*ausP*). | *
tsmoothP* | Average of samples in this time window will be used for smoothing the second HOS-CF. |
| *
ausP* | Controls artificial uplift of samples during precise picking. A common local minimum of the smoothed and unsmoothed HOS-CF is found when the previous sample is larger or equal to the current sample times (1+*
ausP*). |
## Precise S pick ## Precise S pick
Parameters used for determination of precise S pick. Parameters used for determination of precise S pick.
Name | Description | Name | Description |
--- | --- |--------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
*tdet2h* | Time window for determination of AR coefficients. | *
*tpred2h* | Time window in which the waveform is predicted using the determined AR parameters. tdet2h* | Time window for determination of AR coefficients. |
*Srecalcwin* | Time window for recalculation of ARH-CF. New CF will be calculated from initial pick +/- *Srecalcwin*. | *
*tsmoothS* | Average of samples in this time window will be used for smoothing the second ARH-CF. tpred2h* | Time window in which the waveform is predicted using the determined AR parameters. |
*ausS* | Controls artificial uplift of samples during precise picking. A common local minimum of the smoothed and unsmoothed ARH-CF is found when the previous sample is larger or equal to the current sample times (1+*ausS*). | *Srecalcwin* | Time window for recalculation of ARH-CF. New CF will be calculated from initial pick +/- *
*pickwinS* | Time window around initial pick in which to look for a precise pick. Srecalcwin*. |
| *
tsmoothS* | Average of samples in this time window will be used for smoothing the second ARH-CF. |
| *
ausS* | Controls artificial uplift of samples during precise picking. A common local minimum of the smoothed and unsmoothed ARH-CF is found when the previous sample is larger or equal to the current sample times (1+*
ausS*). |
| *
pickwinS* | Time window around initial pick in which to look for a precise pick. |
## Pick quality control ## Pick quality control
Parameters used for checking quality and integrity of automatic picks. Parameters used for checking quality and integrity of automatic picks.
Name | Description | Name | Description |
--- | --- |--------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
*minAICPslope* | Initial P picks with a slope lower than this value will be discared. | *
*minAICPSNR* | Initial P picks with a SNR below this value will be discarded. minAICPslope* | Initial P picks with a slope lower than this value will be discared. |
*minAICSslope* | Initial S picks with a slope lower than this value will be discarded. | *
*minAICSSNR* | Initial S picks with a SNR below this value will be discarded. minAICPSNR* | Initial P picks with a SNR below this value will be discarded. |
*minsiglength*, *noisefacor*. *minpercent* | Parameters for checking signal length. In the time window of size *minsiglength* after the initial P pick *minpercent* of samples have to be larger than the RMS value. | *
*zfac* | To recognize misattributed S picks, the RMS amplitude of vertical and horizontal traces are compared. The RMS amplitude of the vertical traces has to be at least *zfac* higher than the RMS amplitude on the horizontal traces for the pick to be accepted as a valid P pick. minAICSslope* | Initial S picks with a slope lower than this value will be discarded. |
*jackfactor* | A P pick is removed if the jackknife pseudo value of the variance of his subgroup is larger than the variance of all picks multiplied with the *jackfactor*. | *
*mdttolerance* | Maximum allowed deviation of P onset times from the median. Value in seconds. minAICSSNR* | Initial S picks with a SNR below this value will be discarded. |
*wdttolerance* | Maximum allowed deviation of S onset times from the line during the Wadati test. Value in seconds. | *minsiglength*, *noisefacor*. *minpercent* | Parameters for checking signal length. In the time window of size *
minsiglength* after the initial P pick *
minpercent* of samples have to be larger than the RMS value. |
| *
zfac* | To recognize misattributed S picks, the RMS amplitude of vertical and horizontal traces are compared. The RMS amplitude of the vertical traces has to be at least *
zfac* higher than the RMS amplitude on the horizontal traces for the pick to be accepted as a valid P pick. |
| *
jackfactor* | A P pick is removed if the jackknife pseudo value of the variance of his subgroup is larger than the variance of all picks multiplied with the *
jackfactor*. |
| *
mdttolerance* | Maximum allowed deviation of P onset times from the median. Value in seconds. |
| *
wdttolerance* | Maximum allowed deviation of S onset times from the line during the Wadati test. Value in seconds. |
## Pick quality determination ## Pick quality determination
Parameters for discrete quality classes. Parameters for discrete quality classes.
Name | Description | Name | Description |
--- | --- |------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
*timeerrorsP* | Width of the time windows in seconds between earliest and latest possible pick which represent the quality classes 0, 1, 2, 3 for P onsets. | *
*timeerrorsS* | Width of the time windows in seconds between earliest and latest possible pick which represent the quality classes 0, 1, 2, 3 for S onsets. timeerrorsP* | Width of the time windows in seconds between earliest and latest possible pick which represent the quality classes 0, 1, 2, 3 for P onsets. |
*nfacP*, *nfacS* | For determination of latest possible onset time. The time when the signal reaches an amplitude of *nfac* * mean value of the RMS amplitude in the time window *tnoise* corresponds to the latest possible onset time. | *
timeerrorsS* | Width of the time windows in seconds between earliest and latest possible pick which represent the quality classes 0, 1, 2, 3 for S onsets. |
| *nfacP*, *nfacS* | For determination of latest possible onset time. The time when the signal reaches an amplitude of *
nfac* * mean value of the RMS amplitude in the time window *tnoise* corresponds to the latest possible onset time. |
Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

+109772 -109081
View File
File diff suppressed because it is too large Load Diff
+10 -114
View File
@@ -1,118 +1,14 @@
name: pylot_py35 name: pylot_38
channels: channels:
- conda-forge - conda-forge
- defaults - defaults
dependencies: dependencies:
- _libgcc_mutex=0.1=conda_forge - cartopy=0.20.2
- _openmp_mutex=4.5=1_gnu - matplotlib-base=3.3.4
- brotlipy=0.7.0=py36h8f6f2f9_1001 - numpy=1.22.3
- c-ares=1.17.1=h7f98852_1 - obspy=1.3.0
- ca-certificates=2021.5.30=ha878542_0 - pyqtgraph=0.12.4
- cartopy=0.18.0=py36h104b3a8_13 - pyside2>=5.13.2
- certifi=2021.5.30=py36h5fab9bb_0 - python=3.8.12
- cffi=1.14.5=py36hc120d54_0 - qt>=5.12.9
- chardet=4.0.0=py36h5fab9bb_1 - scipy=1.8.0
- cryptography=3.4.6=py36hb60f036_0
- cycler=0.10.0=py_2
- dbus=1.13.6=hfdff14a_1
- decorator=4.4.2=py_0
- expat=2.2.10=h9c3ff4c_0
- fontconfig=2.13.1=hba837de_1004
- freetype=2.10.4=h0708190_1
- future=0.18.2=py36h5fab9bb_3
- geos=3.9.1=h9c3ff4c_2
- gettext=0.19.8.1=h0b5b191_1005
- glib=2.68.0=h9c3ff4c_1
- glib-tools=2.68.0=h9c3ff4c_1
- greenlet=1.0.0=py36hc4f0c31_0
- gst-plugins-base=1.18.4=h29181c9_0
- gstreamer=1.18.4=h76c114f_0
- icu=68.1=h58526e2_0
- idna=2.10=pyh9f0ad1d_0
- importlib-metadata=3.7.3=py36h5fab9bb_0
- jpeg=9d=h36c2ea0_0
- kiwisolver=1.3.1=py36h605e78d_1
- krb5=1.17.2=h926e7f8_0
- lcms2=2.12=hddcbb42_0
- ld_impl_linux-64=2.35.1=hea4e1c9_2
- libblas=3.9.0=8_openblas
- libcblas=3.9.0=8_openblas
- libclang=11.1.0=default_ha53f305_0
- libcurl=7.75.0=hc4aaa36_0
- libedit=3.1.20191231=he28a2e2_2
- libev=4.33=h516909a_1
- libevent=2.1.10=hcdb4288_3
- libffi=3.3=h58526e2_2
- libgcc-ng=9.3.0=h2828fa1_18
- libgfortran-ng=9.3.0=hff62375_18
- libgfortran5=9.3.0=hff62375_18
- libglib=2.68.0=h3e27bee_1
- libgomp=9.3.0=h2828fa1_18
- libiconv=1.16=h516909a_0
- liblapack=3.9.0=8_openblas
- libllvm11=11.1.0=hf817b99_0
- libnghttp2=1.43.0=h812cca2_0
- libopenblas=0.3.12=pthreads_h4812303_1
- libpng=1.6.37=h21135ba_2
- libpq=13.1=hfd2b0eb_2
- libssh2=1.9.0=ha56f1ee_6
- libstdcxx-ng=9.3.0=h6de172a_18
- libtiff=4.2.0=hdc55705_0
- libuuid=2.32.1=h7f98852_1000
- libwebp-base=1.2.0=h7f98852_2
- libxcb=1.13=h7f98852_1003
- libxkbcommon=1.0.3=he3ba5ed_0
- libxml2=2.9.10=h72842e0_3
- libxslt=1.1.33=h15afd5d_2
- lxml=4.6.2=py36h04a5ba7_1
- lz4-c=1.9.3=h9c3ff4c_0
- matplotlib-base=3.3.4=py36hd391965_0
- mysql-common=8.0.23=ha770c72_1
- mysql-libs=8.0.23=h935591d_1
- ncurses=6.2=h58526e2_4
- nspr=4.30=h9c3ff4c_0
- nss=3.63=hb5efdd6_0
- numpy=1.19.5=py36h2aa4a07_1
- obspy=1.2.2=py36h785e9b2_0
- olefile=0.46=pyh9f0ad1d_1
- openssl=1.1.1k=h7f98852_0
- pandas=1.1.5=py36h284efc9_0
- pcre=8.44=he1b5a44_0
- pillow=8.1.2=py36ha6010c0_0
- pip=21.0.1=pyhd8ed1ab_0
- proj=7.2.0=h277dcde_2
- pthread-stubs=0.4=h36c2ea0_1001
- pycparser=2.20=pyh9f0ad1d_2
- pyopenssl=20.0.1=pyhd8ed1ab_0
- pyparsing=2.4.7=pyh9f0ad1d_0
- pyqt5-sip=4.19.18=py36hc4f0c31_7
- pyqtgraph=0.11.1=pyhd3deb0d_0
- pyshp=2.1.3=pyh44b312d_0
- pyside2=5.13.2=py36h6b97533_4
- pysocks=1.7.1=py36h5fab9bb_3
- python=3.6.13=hffdb5ce_0_cpython
- python-dateutil=2.8.1=py_0
- python_abi=3.6=1_cp36m
- pytz=2021.1=pyhd8ed1ab_0
- qt=5.12.9=hda022c4_4
- qtpy=1.9.0=py_0
- readline=8.0=he28a2e2_2
- requests=2.25.1=pyhd3deb0d_0
- scipy=1.5.3=py36h9e8f40b_0
- setuptools=49.6.0=py36h5fab9bb_3
- shapely=1.7.1=py36h93b233e_4
- six=1.15.0=pyh9f0ad1d_0
- sqlalchemy=1.4.2=py36h8f6f2f9_0
- sqlite=3.34.0=h74cdb3f_0
- tk=8.6.10=h21135ba_1
- tornado=6.1=py36h8f6f2f9_1
- typing_extensions=3.7.4.3=py_0
- urllib3=1.26.4=pyhd8ed1ab_0
- wheel=0.36.2=pyhd3deb0d_0
- xorg-libxau=1.0.9=h7f98852_0
- xorg-libxdmcp=1.1.3=h7f98852_0
- xz=5.2.5=h516909a_1
- zipp=3.4.1=pyhd8ed1ab_0
- zlib=1.2.11=h516909a_1010
- zstd=1.4.9=ha95c52a_0
prefix:
+57 -13
View File
@@ -144,6 +144,10 @@ class Magnitude(object):
azimuthal_gap=self.origin_id.get_referred_object().quality.azimuthal_gap) azimuthal_gap=self.origin_id.get_referred_object().quality.azimuthal_gap)
else: else:
# no scaling necessary # no scaling necessary
# Temporary fix needs rework
if (len(self.magnitudes.keys()) == 0):
print("Error in local magnitude calculation ")
return None
mag = ope.Magnitude( mag = ope.Magnitude(
mag=np.median([M.mag for M in self.magnitudes.values()]), mag=np.median([M.mag for M in self.magnitudes.values()]),
magnitude_type=self.type, magnitude_type=self.type,
@@ -223,13 +227,13 @@ class LocalMagnitude(Magnitude):
in 'Z3'] in 'Z3']
# checking horizontal count and calculating power_sum accordingly # checking horizontal count and calculating power_sum accordingly
if len(power) == 1: if len(power) == 1:
print ('WARNING: Only one horizontal found for station {0}.'.format(st[0].stats.station)) print('WARNING: Only one horizontal found for station {0}.'.format(st[0].stats.station))
power_sum = power[0] power_sum = power[0]
elif len(power) == 2: elif len(power) == 2:
power_sum = power[0] + power[1] power_sum = power[0] + power[1]
else: else:
raise ValueError('Wood-Anderson aomplitude defintion only valid for' raise ValueError('Wood-Anderson aomplitude defintion only valid for'
' up to two horizontals: {0} given'.format(len(power))) ' up to two horizontals: {0} given'.format(len(power)))
sqH = np.sqrt(power_sum) sqH = np.sqrt(power_sum)
@@ -325,7 +329,7 @@ class LocalMagnitude(Magnitude):
if self.verbose: if self.verbose:
print( print(
"Local Magnitude for station {0}: ML = {1:3.1f}".format( "Local Magnitude for station {0}: ML = {1:3.1f}".format(
station, magnitude.mag)) station, magnitude.mag))
magnitude.origin_id = self.origin_id magnitude.origin_id = self.origin_id
magnitude.waveform_id = pick.waveform_id magnitude.waveform_id = pick.waveform_id
magnitude.amplitude_id = amplitude.resource_id magnitude.amplitude_id = amplitude.resource_id
@@ -404,7 +408,7 @@ class MomentMagnitude(Magnitude):
if not wf: if not wf:
continue continue
try: try:
scopy = wf.copy() scopy = wf.copy()
except AssertionError: except AssertionError:
print("WARNING: Something's wrong with the data," print("WARNING: Something's wrong with the data,"
"station {}," "station {},"
@@ -414,6 +418,10 @@ class MomentMagnitude(Magnitude):
distance = degrees2kilometers(a.distance) distance = degrees2kilometers(a.distance)
azimuth = a.azimuth azimuth = a.azimuth
incidence = a.takeoff_angle incidence = a.takeoff_angle
if not 0. <= incidence <= 360.:
if self.verbose:
print(f'WARNING: Incidence angle outside bounds - {incidence}')
return
w0, fc = calcsourcespec(scopy, onset, self.p_velocity, distance, w0, fc = calcsourcespec(scopy, onset, self.p_velocity, distance,
azimuth, incidence, self.p_attenuation, azimuth, incidence, self.p_attenuation,
self.plot_flag, self.verbose) self.plot_flag, self.verbose)
@@ -432,6 +440,40 @@ class MomentMagnitude(Magnitude):
self.event.station_magnitudes.append(magnitude) self.event.station_magnitudes.append(magnitude)
self.magnitudes = (station, magnitude) self.magnitudes = (station, magnitude)
# WIP JG
def getSourceSpec(self):
for a in self.arrivals:
if a.phase not in 'pP':
continue
# make sure calculating Mo only from reliable onsets
# NLLoc: time_weight = 0 => do not use onset!
if a.time_weight == 0:
continue
pick = a.pick_id.get_referred_object()
station = pick.waveform_id.station_code
if len(self.stream) <= 2:
print("Station:" '{0}'.format(station))
print("WARNING: No instrument corrected data available,"
" no magnitude calculation possible! Go on.")
continue
wf = self.stream.select(station=station)
if not wf:
continue
try:
scopy = wf.copy()
except AssertionError:
print("WARNING: Something's wrong with the data,"
"station {},"
"no calculation of moment magnitude possible! Go on.".format(station))
continue
onset = pick.time
distance = degrees2kilometers(a.distance)
azimuth = a.azimuth
incidence = a.takeoff_angle
w0, fc, plt = calcsourcespec(scopy, onset, self.p_velocity, distance,
azimuth, incidence, self.p_attenuation,
3, self.verbose)
return w0, fc, plt
def calcMoMw(wfstream, w0, rho, vp, delta, verbosity=False): def calcMoMw(wfstream, w0, rho, vp, delta, verbosity=False):
''' '''
@@ -594,15 +636,15 @@ def calcsourcespec(wfstream, onset, vp, delta, azimuth, incidence,
# fft # fft
fny = freq / 2 fny = freq / 2
#l = len(xdat) / freq # l = len(xdat) / freq
# number of fft bins after Bath # number of fft bins after Bath
#n = freq * l # n = freq * l
# find next power of 2 of data length # find next power of 2 of data length
m = pow(2, np.ceil(np.log(len(xdat)) / np.log(2))) m = pow(2, np.ceil(np.log(len(xdat)) / np.log(2)))
N = min(int(np.power(m, 2)), 16384) N = min(int(np.power(m, 2)), 16384)
#N = int(np.power(m, 2)) # N = int(np.power(m, 2))
y = dt * np.fft.fft(xdat, N) y = dt * np.fft.fft(xdat, N)
Y = abs(y[: N / 2]) Y = abs(y[: int(N / 2)])
L = (N - 1) / freq L = (N - 1) / freq
f = np.arange(0, fny, 1 / L) f = np.arange(0, fny, 1 / L)
@@ -643,8 +685,8 @@ def calcsourcespec(wfstream, onset, vp, delta, azimuth, incidence,
w0 = np.median([w01, w02]) w0 = np.median([w01, w02])
Fc = np.median([fc1, fc2]) Fc = np.median([fc1, fc2])
if verbosity: if verbosity:
print("calcsourcespec: Using w0-value = %e m/Hz and fc = %f Hz" % ( print("calcsourcespec: Using w0-value = %e m/Hz and fc = %f Hz" % (
w0, Fc)) w0, Fc))
if iplot >= 1: if iplot >= 1:
f1 = plt.figure() f1 = plt.figure()
tLdat = np.arange(0, len(Ldat) * dt, dt) tLdat = np.arange(0, len(Ldat) * dt, dt)
@@ -679,6 +721,8 @@ def calcsourcespec(wfstream, onset, vp, delta, azimuth, incidence,
plt.xlabel('Frequency [Hz]') plt.xlabel('Frequency [Hz]')
plt.ylabel('Amplitude [m/Hz]') plt.ylabel('Amplitude [m/Hz]')
plt.grid() plt.grid()
if iplot == 3:
return w0, Fc, plt
plt.show() plt.show()
try: try:
input() input()
+58 -27
View File
@@ -3,16 +3,16 @@
import copy import copy
import os import os
from PySide2.QtWidgets import QMessageBox
from obspy import read_events from obspy import read_events
from obspy.core import read, Stream, UTCDateTime from obspy.core import read, Stream, UTCDateTime
from obspy.core.event import Event as ObsPyEvent from obspy.core.event import Event as ObsPyEvent
from obspy.io.sac import SacIOError from obspy.io.sac import SacIOError
from PySide2.QtWidgets import QMessageBox
import pylot.core.loc.velest as velest
import pylot.core.loc.focmec as focmec import pylot.core.loc.focmec as focmec
import pylot.core.loc.hypodd as hypodd import pylot.core.loc.hypodd as hypodd
import pylot.core.loc.velest as velest
from pylot.core.io.phases import readPILOTEvent, picks_from_picksdict, \ from pylot.core.io.phases import readPILOTEvent, picks_from_picksdict, \
picksdict_from_pilot, merge_picks, PylotParameter picksdict_from_pilot, merge_picks, PylotParameter
from pylot.core.util.errors import FormatError, OverwriteError from pylot.core.util.errors import FormatError, OverwriteError
@@ -100,7 +100,7 @@ class Data(object):
old_pick.phase_hint == new_pick.phase_hint, old_pick.phase_hint == new_pick.phase_hint,
old_pick.method_id == new_pick.method_id] old_pick.method_id == new_pick.method_id]
if all(comparison): if all(comparison):
del(old_pick) del (old_pick)
old_picks.append(new_pick) old_picks.append(new_pick)
elif not other.isNew() and self.isNew(): elif not other.isNew() and self.isNew():
new = other + self new = other + self
@@ -112,7 +112,7 @@ class Data(object):
return self + other return self + other
else: else:
raise ValueError("both Data objects have differing " raise ValueError("both Data objects have differing "
"unique Event identifiers") "unique Event identifiers")
return self return self
def getPicksStr(self): def getPicksStr(self):
@@ -260,7 +260,6 @@ class Data(object):
can be a str or a list of strings of ['manual', 'auto', 'origin', 'magnitude'] can be a str or a list of strings of ['manual', 'auto', 'origin', 'magnitude']
""" """
from pylot.core.util.defaults import OUTPUTFORMATS from pylot.core.util.defaults import OUTPUTFORMATS
if not type(fcheck) == list: if not type(fcheck) == list:
fcheck = [fcheck] fcheck = [fcheck]
@@ -321,35 +320,61 @@ class Data(object):
if lendiff != 0: if lendiff != 0:
print("Manual as well as automatic picks available. Prefered the {} manual ones!".format(lendiff)) print("Manual as well as automatic picks available. Prefered the {} manual ones!".format(lendiff))
no_uncertainties_p = []
no_uncertainties_s = []
if upperErrors: if upperErrors:
# check for pick uncertainties exceeding adjusted upper errors # check for pick uncertainties exceeding adjusted upper errors
# Picks with larger uncertainties will not be saved in output file! # Picks with larger uncertainties will not be saved in output file!
for j in range(len(picks)): for j in range(len(picks)):
for i in range(len(picks_copy)): for i in range(len(picks_copy)):
if picks_copy[i].phase_hint[0] == 'P': if picks_copy[i].phase_hint[0] == 'P':
if (picks_copy[i].time_errors['upper_uncertainty'] >= upperErrors[0]) or \ # Skipping pick if no upper_uncertainty is found and warning user
(picks_copy[i].time_errors['uncertainty'] is None): if picks_copy[i].time_errors['upper_uncertainty'] is None:
#print("{1} P-Pick of station {0} does not have upper_uncertainty and cant be checked".format(
# picks_copy[i].waveform_id.station_code,
# picks_copy[i].method_id))
if not picks_copy[i].waveform_id.station_code in no_uncertainties_p:
no_uncertainties_p.append(picks_copy[i].waveform_id.station_code)
continue
#print ("checking for upper_uncertainty")
if (picks_copy[i].time_errors['uncertainty'] is None) or \
(picks_copy[i].time_errors['upper_uncertainty'] >= upperErrors[0]):
print("Uncertainty exceeds or equal adjusted upper time error!") print("Uncertainty exceeds or equal adjusted upper time error!")
print("Adjusted uncertainty: {}".format(upperErrors[0])) print("Adjusted uncertainty: {}".format(upperErrors[0]))
print("Pick uncertainty: {}".format(picks_copy[i].time_errors['uncertainty'])) print("Pick uncertainty: {}".format(picks_copy[i].time_errors['uncertainty']))
print("{1} P-Pick of station {0} will not be saved in outputfile".format( print("{1} P-Pick of station {0} will not be saved in outputfile".format(
picks_copy[i].waveform_id.station_code, picks_copy[i].waveform_id.station_code,
picks_copy[i].method_id)) picks_copy[i].method_id))
print("#")
del picks_copy[i] del picks_copy[i]
break break
if picks_copy[i].phase_hint[0] == 'S': if picks_copy[i].phase_hint[0] == 'S':
if (picks_copy[i].time_errors['upper_uncertainty'] >= upperErrors[1]) or \
(picks_copy[i].time_errors['uncertainty'] is None): # Skipping pick if no upper_uncertainty is found and warning user
if picks_copy[i].time_errors['upper_uncertainty'] is None:
#print("{1} S-Pick of station {0} does not have upper_uncertainty and cant be checked".format(
#picks_copy[i].waveform_id.station_code,
#picks_copy[i].method_id))
if not picks_copy[i].waveform_id.station_code in no_uncertainties_s:
no_uncertainties_s.append(picks_copy[i].waveform_id.station_code)
continue
if (picks_copy[i].time_errors['uncertainty'] is None) or \
(picks_copy[i].time_errors['upper_uncertainty'] >= upperErrors[1]):
print("Uncertainty exceeds or equal adjusted upper time error!") print("Uncertainty exceeds or equal adjusted upper time error!")
print("Adjusted uncertainty: {}".format(upperErrors[1])) print("Adjusted uncertainty: {}".format(upperErrors[1]))
print("Pick uncertainty: {}".format(picks_copy[i].time_errors['uncertainty'])) print("Pick uncertainty: {}".format(picks_copy[i].time_errors['uncertainty']))
print("{1} S-Pick of station {0} will not be saved in outputfile".format( print("{1} S-Pick of station {0} will not be saved in outputfile".format(
picks_copy[i].waveform_id.station_code, picks_copy[i].waveform_id.station_code,
picks_copy[i].method_id)) picks_copy[i].method_id))
print("#")
del picks_copy[i] del picks_copy[i]
break break
for s in no_uncertainties_p:
print("P-Pick of station {0} does not have upper_uncertainty and cant be checked".format(s))
for s in no_uncertainties_s:
print("S-Pick of station {0} does not have upper_uncertainty and cant be checked".format(s))
if fnext == '.obs': if fnext == '.obs':
try: try:
@@ -360,13 +385,13 @@ class Data(object):
nllocfile = open(fnout + fnext) nllocfile = open(fnout + fnext)
l = nllocfile.readlines() l = nllocfile.readlines()
# Adding A0/Generic Amplitude to .obs file # Adding A0/Generic Amplitude to .obs file
#l2 = [] # l2 = []
#for li in l: # for li in l:
# for amp in evtdata_org.amplitudes: # for amp in evtdata_org.amplitudes:
# if amp.waveform_id.station_code == li[0:5].strip(): # if amp.waveform_id.station_code == li[0:5].strip():
# li = li[0:64] + '{:0.2e}'.format(amp.generic_amplitude) + li[73:-1] + '\n' # li = li[0:64] + '{:0.2e}'.format(amp.generic_amplitude) + li[73:-1] + '\n'
# l2.append(li) # l2.append(li)
#l = l2 # l = l2
nllocfile.close() nllocfile.close()
l.insert(0, header) l.insert(0, header)
nllocfile = open(fnout + fnext, 'w') nllocfile = open(fnout + fnext, 'w')
@@ -426,20 +451,25 @@ class Data(object):
data.filter(**kwargs) data.filter(**kwargs)
self.dirty = True self.dirty = True
def setWFData(self, fnames, fnames_syn=None, checkRotated=False, metadata=None, tstart=0, tstop=0): def setWFData(self, fnames, fnames_alt=None, checkRotated=False, metadata=None, tstart=0, tstop=0):
""" """
Clear current waveform data and set given waveform data Clear current waveform data and set given waveform data
:param fnames: waveform data names to append :param fnames: waveform data names to append
:param fnames_alt: alternative data to show (e.g. synthetic/processed)
:type fnames: list :type fnames: list
""" """
self.wfdata = Stream() self.wfdata = Stream()
self.wforiginal = None self.wforiginal = None
self.wfsyn = Stream() self.wf_alt = Stream()
if tstart == tstop: if tstart == tstop:
tstart = tstop = None tstart = tstop = None
self.tstart = tstart self.tstart = tstart
self.tstop = tstop self.tstop = tstop
# remove directories
fnames = [fname for fname in fnames if not os.path.isdir(fname)]
fnames_alt = [fname for fname in fnames_alt if not os.path.isdir(fname)]
# if obspy_dmt: # if obspy_dmt:
# wfdir = 'raw' # wfdir = 'raw'
# self.processed = False # self.processed = False
@@ -457,8 +487,8 @@ class Data(object):
# wffnames = fnames # wffnames = fnames
if fnames is not None: if fnames is not None:
self.appendWFData(fnames) self.appendWFData(fnames)
if fnames_syn is not None: if fnames_alt is not None:
self.appendWFData(fnames_syn, synthetic=True) self.appendWFData(fnames_alt, alternative=True)
else: else:
return False return False
@@ -478,7 +508,7 @@ class Data(object):
self.dirty = False self.dirty = False
return True return True
def appendWFData(self, fnames, synthetic=False): def appendWFData(self, fnames, alternative=False):
""" """
Read waveform data from fnames and append it to current wf data Read waveform data from fnames and append it to current wf data
:param fnames: waveform data to append :param fnames: waveform data to append
@@ -491,19 +521,20 @@ class Data(object):
if self.dirty: if self.dirty:
self.resetWFData() self.resetWFData()
real_or_syn_data = {True: self.wfsyn, orig_or_alternative_data = {True: self.wf_alt,
False: self.wfdata} False: self.wfdata}
warnmsg = '' warnmsg = ''
for fname in set(fnames): for fname in set(fnames):
try: try:
real_or_syn_data[synthetic] += read(fname, starttime=self.tstart, endtime=self.tstop) orig_or_alternative_data[alternative] += read(fname, starttime=self.tstart, endtime=self.tstop)
except TypeError: except TypeError:
try: try:
real_or_syn_data[synthetic] += read(fname, format='GSE2', starttime=self.tstart, endtime=self.tstop) orig_or_alternative_data[alternative] += read(fname, format='GSE2', starttime=self.tstart, endtime=self.tstop)
except Exception as e: except Exception as e:
try: try:
real_or_syn_data[synthetic] += read(fname, format='SEGY', starttime=self.tstart, endtime=self.tstop) orig_or_alternative_data[alternative] += read(fname, format='SEGY', starttime=self.tstart,
endtime=self.tstop)
except Exception as e: except Exception as e:
warnmsg += '{0}\n{1}\n'.format(fname, e) warnmsg += '{0}\n{1}\n'.format(fname, e)
except SacIOError as se: except SacIOError as se:
@@ -518,8 +549,8 @@ class Data(object):
def getOriginalWFData(self): def getOriginalWFData(self):
return self.wforiginal return self.wforiginal
def getSynWFData(self): def getAltWFdata(self):
return self.wfsyn return self.wf_alt
def resetWFData(self): def resetWFData(self):
""" """
+3 -3
View File
@@ -515,9 +515,9 @@ defaults = {'rootpath': {'type': str,
'namestring': 'TauPy model'}, 'namestring': 'TauPy model'},
'taup_phases': {'type': str, 'taup_phases': {'type': str,
'tooltip': 'Specify possible phases for TauPy (comma separated). See Obspy TauPy documentation for possible values.', 'tooltip': 'Specify possible phases for TauPy (comma separated). See Obspy TauPy documentation for possible values.',
'value': 'ttall', 'value': 'ttall',
'namestring': 'TauPy phases'}, 'namestring': 'TauPy phases'},
} }
settings_main = { settings_main = {
+17 -12
View File
@@ -8,14 +8,15 @@
Edited for use in PyLoT Edited for use in PyLoT
JG, igem, 01/2022 JG, igem, 01/2022
""" """
import pdb
import os import os
import argparse import argparse
import numpy as np import numpy as np
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
import glob
from obspy.core.event import read_events from obspy.core.event import read_events
from pyproj import Proj from pyproj import Proj
import glob
""" """
Creates an eventlist file summarizing all events found in a certain folder. Only called by pressing UI Button eventlis_xml_action Creates an eventlist file summarizing all events found in a certain folder. Only called by pressing UI Button eventlis_xml_action
@@ -24,14 +25,15 @@ Creates an eventlist file summarizing all events found in a certain folder. Only
:param path: Path to root folder where single Event folder are to found :param path: Path to root folder where single Event folder are to found
""" """
def geteventlistfromxml(path, outpath): def geteventlistfromxml(path, outpath):
p = Proj(proj='utm', zone=32, ellps='WGS84') p = Proj(proj='utm', zone=32, ellps='WGS84')
# open eventlist file and write header # open eventlist file and write header
evlist = outpath + '/eventlist' evlist = outpath + '/eventlist'
evlistobj = open(evlist, 'w') evlistobj = open(evlist, 'w')
evlistobj.write('EventID Date To Lat Lon EAST NORTH Dep Ml NoP NoS RMS errH errZ Gap \n') evlistobj.write(
'EventID Date To Lat Lon EAST NORTH Dep Ml NoP NoS RMS errH errZ Gap \n')
# data path # data path
dp = path + "/e*/*.xml" dp = path + "/e*/*.xml"
@@ -52,7 +54,8 @@ def geteventlistfromxml(path, outpath):
NoP = [] NoP = []
NoS = [] NoS = []
except IndexError: except IndexError:
print ('Insufficient data found for event (not localised): ' + names.split('/')[-1].split('_')[-1][:-4] + ' Skipping event for eventlist.' ) print('Insufficient data found for event (not localised): ' + names.split('/')[-1].split('_')[-1][
:-4] + ' Skipping event for eventlist.')
continue continue
for i in range(len(cat.events[0].origins[0].arrivals)): for i in range(len(cat.events[0].origins[0].arrivals)):
@@ -60,20 +63,22 @@ def geteventlistfromxml(path, outpath):
NoP.append(cat.events[0].origins[0].arrivals[i].phase) NoP.append(cat.events[0].origins[0].arrivals[i].phase)
elif cat.events[0].origins[0].arrivals[i].phase == 'S': elif cat.events[0].origins[0].arrivals[i].phase == 'S':
NoS.append(cat.events[0].origins[0].arrivals[i].phase) NoS.append(cat.events[0].origins[0].arrivals[i].phase)
#NoP = cat.events[0].origins[0].quality.used_station_count # NoP = cat.events[0].origins[0].quality.used_station_count
errH = cat.events[0].origins[0].origin_uncertainty.max_horizontal_uncertainty errH = cat.events[0].origins[0].origin_uncertainty.max_horizontal_uncertainty
errZ = cat.events[0].origins[0].depth_errors.uncertainty errZ = cat.events[0].origins[0].depth_errors.uncertainty
Gap = cat.events[0].origins[0].quality.azimuthal_gap Gap = cat.events[0].origins[0].quality.azimuthal_gap
#evID = names.split('/')[6] # evID = names.split('/')[6]
evID = names.split('/')[-1].split('_')[-1][:-4] evID = names.split('/')[-1].split('_')[-1][:-4]
Date = str(st.year) + str('%02d' % st.month) + str('%02d' % st.day) Date = str(st.year) + str('%02d' % st.month) + str('%02d' % st.day)
To = str('%02d' % st.hour) + str('%02d' % st.minute) + str('%02d' % st.second) + \ To = str('%02d' % st.hour) + str('%02d' % st.minute) + str('%02d' % st.second) + \
'.' + str('%06d' % st.microsecond) '.' + str('%06d' % st.microsecond)
# write into eventlist # write into eventlist
evlistobj.write('%s %s %s %9.6f %9.6f %13.6f %13.6f %8.6f %3.1f %d %d NaN %d %d %d\n' %(evID, \ evlistobj.write('%s %s %s %9.6f %9.6f %13.6f %13.6f %8.6f %3.1f %d %d NaN %d %d %d\n' % (evID, \
Date, To, Lat, Lon, EAST, NORTH, Dep, Ml, len(NoP), len(NoS), errH, errZ, Gap)) Date, To, Lat, Lon,
print ('Adding Event ' + names.split('/')[-1].split('_')[-1][:-4] + ' to eventlist') EAST, NORTH, Dep, Ml,
len(NoP), len(NoS),
errH, errZ, Gap))
print('Adding Event ' + names.split('/')[-1].split('_')[-1][:-4] + ' to eventlist')
print('Eventlist created and saved in: ' + outpath) print('Eventlist created and saved in: ' + outpath)
evlistobj.close() evlistobj.close()
-139
View File
@@ -1,139 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
"""
Script to get onset uncertainties from Quakeml.xml files created by PyLoT.
Uncertainties are tranformed into quality classes and visualized via histogram if desired.
Ludger Küperkoch, BESTEC GmbH, 07/2017
rev.: Ludger Küperkoch, igem, 10/2020
Edited for usage in PyLoT: Jeldrik Gaal, igem, 01/2022
"""
import argparse
import numpy as np
import matplotlib.pyplot as plt
from obspy.core.event import read_events
import glob
def getQualitiesfromxml(path):
# uncertainties
ErrorsP = [0.02, 0.04, 0.08, 0.16]
ErrorsS = [0.04, 0.08, 0.16, 0.32]
Pw0 = []
Pw1 = []
Pw2 = []
Pw3 = []
Pw4 = []
Sw0 = []
Sw1 = []
Sw2 = []
Sw3 = []
Sw4 = []
# data path
dp = path + '/e*/*.xml'
# list of all available xml-files
xmlnames = glob.glob(dp)
# read all onset weights
for names in xmlnames:
print("Getting onset weights from {}".format(names))
cat = read_events(names)
arrivals = cat.events[0].picks
for Pick in arrivals:
if Pick.phase_hint[0] == 'P':
if Pick.time_errors.uncertainty <= ErrorsP[0]:
Pw0.append(Pick.time_errors.uncertainty)
elif Pick.time_errors.uncertainty > ErrorsP[0] and \
Pick.time_errors.uncertainty <= ErrorsP[1]:
Pw1.append(Pick.time_errors.uncertainty)
elif Pick.time_errors.uncertainty > ErrorsP[1] and \
Pick.time_errors.uncertainty <= ErrorsP[2]:
Pw2.append(Pick.time_errors.uncertainty)
elif Pick.time_errors.uncertainty > ErrorsP[2] and \
Pick.time_errors.uncertainty <= ErrorsP[3]:
Pw3.append(Pick.time_errors.uncertainty)
elif Pick.time_errors.uncertainty > ErrorsP[3]:
Pw4.append(Pick.time_errors.uncertainty)
else:
pass
elif Pick.phase_hint[0] == 'S':
if Pick.time_errors.uncertainty <= ErrorsS[0]:
Sw0.append(Pick.time_errors.uncertainty)
elif Pick.time_errors.uncertainty > ErrorsS[0] and \
Pick.time_errors.uncertainty <= ErrorsS[1]:
Sw1.append(Pick.time_errors.uncertainty)
elif Pick.time_errors.uncertainty > ErrorsS[1] and \
Pick.time_errors.uncertainty <= ErrorsS[2]:
Sw2.append(Pick.time_errors.uncertainty)
elif Pick.time_errors.uncertainty > ErrorsS[2] and \
Pick.time_errors.uncertainty <= ErrorsS[3]:
Sw3.append(Pick.time_errors.uncertainty)
elif Pick.time_errors.uncertainty > ErrorsS[3]:
Sw4.append(Pick.time_errors.uncertainty)
else:
pass
else:
print("Phase hint not defined for picking!")
pass
# get percentage of weights
numPweights = np.sum([len(Pw0), len(Pw1), len(Pw2), len(Pw3), len(Pw4)])
numSweights = np.sum([len(Sw0), len(Sw1), len(Sw2), len(Sw3), len(Sw4)])
try:
P0perc = 100.0 / numPweights * len(Pw0)
except:
P0perc = 0
try:
P1perc = 100.0 / numPweights * len(Pw1)
except:
P1perc = 0
try:
P2perc = 100.0 / numPweights * len(Pw2)
except:
P2perc = 0
try:
P3perc = 100.0 / numPweights * len(Pw3)
except:
P3perc = 0
try:
P4perc = 100.0 / numPweights * len(Pw4)
except:
P4perc = 0
try:
S0perc = 100.0 / numSweights * len(Sw0)
except:
Soperc = 0
try:
S1perc = 100.0 / numSweights * len(Sw1)
except:
S1perc = 0
try:
S2perc = 100.0 / numSweights * len(Sw2)
except:
S2perc = 0
try:
S3perc = 100.0 / numSweights * len(Sw3)
except:
S3perc = 0
try:
S4perc = 100.0 / numSweights * len(Sw4)
except:
S4perc = 0
weights = ('0', '1', '2', '3', '4')
y_pos = np.arange(len(weights))
width = 0.34
p1, = plt.bar(0 - width, P0perc, width, color='black')
p2, = plt.bar(0, S0perc, width, color='red')
plt.bar(y_pos - width, [P0perc, P1perc, P2perc, P3perc, P4perc], width, color='black')
plt.bar(y_pos, [S0perc, S1perc, S2perc, S3perc, S4perc], width, color='red')
plt.ylabel('%')
plt.xticks(y_pos, weights)
plt.xlim([-0.5, 4.5])
plt.xlabel('Qualities')
plt.title('{0} P-Qualities, {1} S-Qualities'.format(numPweights, numSweights))
plt.legend([p1, p2], ['P-Weights', 'S-Weights'])
plt.show()
+127 -161
View File
@@ -1,13 +1,13 @@
#!/usr/bin/env python #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import pdb
import glob import glob
import os
import warnings
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
import numpy as np import numpy as np
import obspy.core.event as ope import obspy.core.event as ope
import os
import scipy.io as sio import scipy.io as sio
import warnings
from obspy.core import UTCDateTime from obspy.core import UTCDateTime
from obspy.core.event import read_events from obspy.core.event import read_events
from obspy.core.util import AttribDict from obspy.core.util import AttribDict
@@ -17,7 +17,7 @@ from pylot.core.io.location import create_event, \
create_magnitude create_magnitude
from pylot.core.pick.utils import select_for_phase, get_quality_class from pylot.core.pick.utils import select_for_phase, get_quality_class
from pylot.core.util.utils import getOwner, full_range, four_digits, transformFilterString4Export, \ from pylot.core.util.utils import getOwner, full_range, four_digits, transformFilterString4Export, \
backtransformFilterString backtransformFilterString, loopIdentifyPhase, identifyPhase
def add_amplitudes(event, amplitudes): def add_amplitudes(event, amplitudes):
@@ -280,6 +280,7 @@ def picksdict_from_picks(evt):
infile = os.path.join(os.path.expanduser('~'), '.pylot', 'pylot.in') infile = os.path.join(os.path.expanduser('~'), '.pylot', 'pylot.in')
print('Using default input file {}'.format(infile)) print('Using default input file {}'.format(infile))
parameter = PylotParameter(infile) parameter = PylotParameter(infile)
pick.phase_hint = identifyPhase(pick.phase_hint)
if pick.phase_hint == 'P': if pick.phase_hint == 'P':
errors = parameter['timeerrorsP'] errors = parameter['timeerrorsP']
elif pick.phase_hint == 'S': elif pick.phase_hint == 'S':
@@ -375,7 +376,6 @@ def picks_from_picksdict(picks, creation_info=None):
def reassess_pilot_db(root_dir, db_dir, out_dir=None, fn_param=None, verbosity=0): def reassess_pilot_db(root_dir, db_dir, out_dir=None, fn_param=None, verbosity=0):
import glob
# TODO: change root to datapath # TODO: change root to datapath
db_root = os.path.join(root_dir, db_dir) db_root = os.path.join(root_dir, db_dir)
evt_list = glob.glob1(db_root, 'e????.???.??') evt_list = glob.glob1(db_root, 'e????.???.??')
@@ -393,9 +393,6 @@ def reassess_pilot_event(root_dir, db_dir, event_id, out_dir=None, fn_param=None
from pylot.core.pick.utils import earllatepicker from pylot.core.pick.utils import earllatepicker
# TODO: change root to datapath # TODO: change root to datapath
if fn_param is None:
fn_param = defaults.AUTOMATIC_DEFAULTS
default = PylotParameter(fn_param, verbosity) default = PylotParameter(fn_param, verbosity)
search_base = os.path.join(root_dir, db_dir, event_id) search_base = os.path.join(root_dir, db_dir, event_id)
@@ -518,10 +515,10 @@ def writephases(arrivals, fformat, filename, parameter=None, eventinfo=None):
# write header # write header
fid.write('# EQEVENT: %s Label: EQ%s Loc: X 0.00 Y 0.00 Z 10.00 OT 0.00 \n' % fid.write('# EQEVENT: %s Label: EQ%s Loc: X 0.00 Y 0.00 Z 10.00 OT 0.00 \n' %
(parameter.get('database'), parameter.get('eventID'))) (parameter.get('database'), parameter.get('eventID')))
arrivals = chooseArrivals(arrivals) arrivals = chooseArrivals(arrivals) # MP MP what is chooseArrivals? It is not defined anywhere
for key in arrivals: for key in arrivals:
# P onsets # P onsets
if arrivals[key].has_key('P'): if 'P' in arrivals[key]:
try: try:
fm = arrivals[key]['P']['fm'] fm = arrivals[key]['P']['fm']
except KeyError as e: except KeyError as e:
@@ -555,7 +552,7 @@ def writephases(arrivals, fformat, filename, parameter=None, eventinfo=None):
ss_ms, ss_ms,
pweight)) pweight))
# S onsets # S onsets
if arrivals[key].has_key('S') and arrivals[key]['S']['mpp'] is not None: if 'S' in arrivals[key] and arrivals[key]['S']['mpp'] is not None:
fm = '?' fm = '?'
onset = arrivals[key]['S']['mpp'] onset = arrivals[key]['S']['mpp']
year = onset.year year = onset.year
@@ -572,20 +569,20 @@ def writephases(arrivals, fformat, filename, parameter=None, eventinfo=None):
sweight = 0 # do not use pick sweight = 0 # do not use pick
except KeyError as e: except KeyError as e:
print(str(e) + '; no weight set during processing') print(str(e) + '; no weight set during processing')
Ao = arrivals[key]['S']['Ao'] # peak-to-peak amplitude Ao = arrivals[key]['S']['Ao'] # peak-to-peak amplitude
if Ao == None: if Ao == None:
Ao = 0.0 Ao = 0.0
#fid.write('%s ? ? ? S %s %d%02d%02d %02d%02d %7.4f GAU 0 0 0 0 %d \n' % (key, # fid.write('%s ? ? ? S %s %d%02d%02d %02d%02d %7.4f GAU 0 0 0 0 %d \n' % (key,
fid.write('%s ? ? ? S %s %d%02d%02d %02d%02d %7.4f GAU 0 %9.2f 0 0 %d \n' % (key, fid.write('%s ? ? ? S %s %d%02d%02d %02d%02d %7.4f GAU 0 %9.2f 0 0 %d \n' % (key,
fm, fm,
year, year,
month, month,
day, day,
hh, hh,
mm, mm,
ss_ms, ss_ms,
Ao, Ao,
sweight)) sweight))
fid.close() fid.close()
elif fformat == 'HYPO71': elif fformat == 'HYPO71':
@@ -594,7 +591,7 @@ def writephases(arrivals, fformat, filename, parameter=None, eventinfo=None):
# write header # write header
fid.write(' %s\n' % fid.write(' %s\n' %
parameter.get('eventID')) parameter.get('eventID'))
arrivals = chooseArrivals(arrivals) arrivals = chooseArrivals(arrivals) # MP MP what is chooseArrivals? It is not defined anywhere
for key in arrivals: for key in arrivals:
if arrivals[key]['P']['weight'] < 4: if arrivals[key]['P']['weight'] < 4:
stat = key stat = key
@@ -671,10 +668,10 @@ def writephases(arrivals, fformat, filename, parameter=None, eventinfo=None):
fid = open("%s" % filename, 'w') fid = open("%s" % filename, 'w')
# write header # write header
fid.write('%s, event %s \n' % (parameter.get('database'), parameter.get('eventID'))) fid.write('%s, event %s \n' % (parameter.get('database'), parameter.get('eventID')))
arrivals = chooseArrivals(arrivals) arrivals = chooseArrivals(arrivals) # MP MP what is chooseArrivals? It is not defined anywhere
for key in arrivals: for key in arrivals:
# P onsets # P onsets
if arrivals[key].has_key('P') and arrivals[key]['P']['mpp'] is not None: if 'P' in arrivals[key] and arrivals[key]['P']['mpp'] is not None:
if arrivals[key]['P']['weight'] < 4: if arrivals[key]['P']['weight'] < 4:
Ponset = arrivals[key]['P']['mpp'] Ponset = arrivals[key]['P']['mpp']
pyear = Ponset.year pyear = Ponset.year
@@ -703,7 +700,7 @@ def writephases(arrivals, fformat, filename, parameter=None, eventinfo=None):
fid.write('%-5s P1 %4.0f %02d %02d %02d %02d %05.02f %5.3f -999. 0.00 -999. 0.00\n' fid.write('%-5s P1 %4.0f %02d %02d %02d %02d %05.02f %5.3f -999. 0.00 -999. 0.00\n'
% (key, pyear, pmonth, pday, phh, pmm, Pss, pstd)) % (key, pyear, pmonth, pday, phh, pmm, Pss, pstd))
# S onsets # S onsets
if arrivals[key].has_key('S') and arrivals[key]['S']['mpp'] is not None: if 'S' in arrivals[key] and arrivals[key]['S']['mpp'] is not None:
if arrivals[key]['S']['weight'] < 4: if arrivals[key]['S']['weight'] < 4:
Sonset = arrivals[key]['S']['mpp'] Sonset = arrivals[key]['S']['mpp']
syear = Sonset.year syear = Sonset.year
@@ -769,10 +766,10 @@ def writephases(arrivals, fformat, filename, parameter=None, eventinfo=None):
arrivals = picksdict_from_picks(evt) arrivals = picksdict_from_picks(evt)
# check for automatic and manual picks # check for automatic and manual picks
# prefer manual picks # prefer manual picks
usedarrivals = chooseArrival(arrivals) usedarrivals = chooseArrivals(arrivals)
for key in usedarrivals: for key in usedarrivals:
# P onsets # P onsets
if usedarrivals[key].has_key('P'): if 'P' in usedarrivals[key]:
if usedarrivals[key]['P']['weight'] < 4: if usedarrivals[key]['P']['weight'] < 4:
n += 1 n += 1
stat = key stat = key
@@ -786,7 +783,7 @@ def writephases(arrivals, fformat, filename, parameter=None, eventinfo=None):
else: else:
fid.write('%-4sP%d%6.2f\n' % (stat, Pweight, Prt)) fid.write('%-4sP%d%6.2f\n' % (stat, Pweight, Prt))
# S onsets # S onsets
if usedarrivals[key].has_key('S'): if 'S' in usedarrivals[key]:
if usedarrivals[key]['S']['weight'] < 4: if usedarrivals[key]['S']['weight'] < 4:
n += 1 n += 1
stat = key stat = key
@@ -815,9 +812,9 @@ def writephases(arrivals, fformat, filename, parameter=None, eventinfo=None):
event = eventinfo['pylot_id'] event = eventinfo['pylot_id']
hddID = event.split('.')[0][1:5] hddID = event.split('.')[0][1:5]
except: except:
print ("Error 1111111!") print("Error 1111111!")
hddID = "00000" hddID = "00000"
# write header # write header
fid.write('# %d %d %d %d %d %5.2f %7.4f +%6.4f %7.4f %4.2f 0.1 0.5 %4.2f %s\n' % ( fid.write('# %d %d %d %d %d %5.2f %7.4f +%6.4f %7.4f %4.2f 0.1 0.5 %4.2f %s\n' % (
stime.year, stime.month, stime.day, stime.hour, stime.minute, stime.second, stime.year, stime.month, stime.day, stime.hour, stime.minute, stime.second,
eventsource['latitude'], eventsource['longitude'], eventsource['depth'] / 1000, eventsource['latitude'], eventsource['longitude'], eventsource['depth'] / 1000,
@@ -830,15 +827,15 @@ def writephases(arrivals, fformat, filename, parameter=None, eventinfo=None):
arrivals = picksdict_from_picks(evt) arrivals = picksdict_from_picks(evt)
# check for automatic and manual picks # check for automatic and manual picks
# prefer manual picks # prefer manual picks
usedarrivals = chooseArrival(arrivals) usedarrivals = chooseArrivals(arrivals)
for key in usedarrivals: for key in usedarrivals:
if usedarrivals[key].has_key('P'): if 'P' in usedarrivals[key]:
# P onsets # P onsets
if usedarrivals[key]['P']['weight'] < 4: if usedarrivals[key]['P']['weight'] < 4:
Ponset = usedarrivals[key]['P']['mpp'] Ponset = usedarrivals[key]['P']['mpp']
Prt = Ponset - stime # onset time relative to source time Prt = Ponset - stime # onset time relative to source time
fid.write('%s %6.3f 1 P\n' % (key, Prt)) fid.write('%s %6.3f 1 P\n' % (key, Prt))
if usedarrivals[key].has_key('S'): if 'S' in usedarrivals[key]:
# S onsets # S onsets
if usedarrivals[key]['S']['weight'] < 4: if usedarrivals[key]['S']['weight'] < 4:
Sonset = usedarrivals[key]['S']['mpp'] Sonset = usedarrivals[key]['S']['mpp']
@@ -881,9 +878,9 @@ def writephases(arrivals, fformat, filename, parameter=None, eventinfo=None):
arrivals = picksdict_from_picks(evt) arrivals = picksdict_from_picks(evt)
# check for automatic and manual picks # check for automatic and manual picks
# prefer manual picks # prefer manual picks
usedarrivals = chooseArrival(arrivals) usedarrivals = chooseArrivals(arrivals)
for key in usedarrivals: for key in usedarrivals:
if usedarrivals[key].has_key('P'): if 'P' in usedarrivals[key]:
if usedarrivals[key]['P']['weight'] < 4 and usedarrivals[key]['P']['fm'] is not None: if usedarrivals[key]['P']['weight'] < 4 and usedarrivals[key]['P']['fm'] is not None:
stat = key stat = key
for i in range(len(picks)): for i in range(len(picks)):
@@ -962,10 +959,10 @@ def writephases(arrivals, fformat, filename, parameter=None, eventinfo=None):
erh, erz, eventinfo.magnitudes[0]['mag'], erh, erz, eventinfo.magnitudes[0]['mag'],
hashID)) hashID))
# Prefer Manual Picks over automatic ones if possible # Prefer Manual Picks over automatic ones if possible
arrivals = chooseArrivals(arrivals) arrivals = chooseArrivals(arrivals) # MP MP what is chooseArrivals? It is not defined anywhere
# write phase lines # write phase lines
for key in arrivals: for key in arrivals:
if arrivals[key].has_key('P'): if 'P' in arrivals[key]:
if arrivals[key]['P']['weight'] < 4 and arrivals[key]['P']['fm'] is not None: if arrivals[key]['P']['weight'] < 4 and arrivals[key]['P']['fm'] is not None:
stat = key stat = key
ccode = arrivals[key]['P']['channel'] ccode = arrivals[key]['P']['channel']
@@ -1009,7 +1006,8 @@ def writephases(arrivals, fformat, filename, parameter=None, eventinfo=None):
fid1.close() fid1.close()
fid2.close() fid2.close()
def chooseArrival(arrivals):
def chooseArrivals(arrivals):
""" """
takes arrivals and returns the manual picks if manual and automatic ones are there takes arrivals and returns the manual picks if manual and automatic ones are there
returns automatic picks if only automatic picks are there returns automatic picks if only automatic picks are there
@@ -1017,7 +1015,6 @@ def chooseArrival(arrivals):
:return: arrivals but with the manual picks prefered if possible :return: arrivals but with the manual picks prefered if possible
""" """
# If len of arrivals is greater than 2 it comes from autopicking so only autopicks are available # If len of arrivals is greater than 2 it comes from autopicking so only autopicks are available
print("=== CHOOSE ===")
if len(arrivals) > 2: if len(arrivals) > 2:
return arrivals return arrivals
if arrivals['auto'] and arrivals['manual']: if arrivals['auto'] and arrivals['manual']:
@@ -1058,37 +1055,63 @@ def merge_picks(event, picks):
return event return event
def getQualitiesfromxml(xmlnames, ErrorsP, ErrorsS, plotflag=1): def getQualitiesfromxml(path, errorsP, errorsS, plotflag=1, figure=None, verbosity=0):
""" """
Script to get onset uncertainties from Quakeml.xml files created by PyLoT. Script to get onset uncertainties from Quakeml.xml files created by PyLoT.
Uncertainties are tranformed into quality classes and visualized via histogram if desired. Uncertainties are tranformed into quality classes and visualized via histogram if desired.
Ludger Küperkoch, BESTEC GmbH, 07/2017 Ludger Küperkoch, BESTEC GmbH, 07/2017
:param xmlnames: list of xml obspy event files containing picks :param path: path containing xml files
:type xmlnames: list :type path: str
:param ErrorsP: time errors of P waves for the four discrete quality classes :param errorsP: time errors of P waves for the four discrete quality classes
:type ErrorsP: :type errorsP:
:param ErrorsS: time errors of S waves for the four discrete quality classes :param errorsS: time errors of S waves for the four discrete quality classes
:type ErrorsS: :type errorsS:
:param plotflag: :param plotflag:
:type plotflag: :type plotflag:
:return: :return:
:rtype: :rtype:
""" """
from pylot.core.pick.utils import get_quality_class def calc_perc(uncertainties, ntotal):
from pylot.core.util.utils import loopIdentifyPhase, identifyPhase ''' simple function that calculates percentage of number of uncertainties (list length)'''
if len(uncertainties) == 0:
return 0
else:
return 100. / ntotal * len(uncertainties)
def calc_weight_perc(psweights, weight_ids):
''' calculate percentages of different weights (pick classes!?) of total number of uncertainties of a phase'''
# count total number of list items for this phase
numWeights = np.sum([len(weight) for weight in psweights.values()])
# iterate over all available weights to return a list with percentages for plotting
plot_list = []
for weight_id in weight_ids:
plot_list.append(calc_perc(psweights[weight_id], numWeights))
return plot_list, numWeights
# get all xmlfiles in path (maybe this should be changed to one xml file for this function, selectable via GUI?)
xmlnames = glob.glob(os.path.join(path, '*.xml'))
if len(xmlnames) == 0:
print(f'No files found in path {path}.')
return False
# first define possible phases here
phases = ['P', 'S']
# define possible weights (0-4)
weight_ids = list(range(5))
# put both error lists in a dictionary with P/S key so that amount of code can be halfed by simply using P/S as key
errors = dict(P=errorsP, S=errorsS)
# create dictionaries for each phase (P/S) with a dictionary of empty list for each weight defined in weights
# tuple above
weights = {}
for phase in phases:
weights[phase] = {weight_id: [] for weight_id in weight_ids}
# read all onset weights
Pw0 = []
Pw1 = []
Pw2 = []
Pw3 = []
Pw4 = []
Sw0 = []
Sw1 = []
Sw2 = []
Sw3 = []
Sw4 = []
for names in xmlnames: for names in xmlnames:
print("Getting onset weights from {}".format(names)) print("Getting onset weights from {}".format(names))
cat = read_events(names) cat = read_events(names)
@@ -1096,117 +1119,60 @@ def getQualitiesfromxml(xmlnames, ErrorsP, ErrorsS, plotflag=1):
arrivals = cat.events[0].picks arrivals = cat.events[0].picks
arrivals_copy = cat_copy.events[0].picks arrivals_copy = cat_copy.events[0].picks
# Prefere manual picks if qualities are sufficient! # Prefere manual picks if qualities are sufficient!
for Pick in arrivals: for pick in arrivals:
if Pick.method_id.id.split('/')[1] == 'manual': if pick.method_id.id.split('/')[1] == 'manual':
mstation = Pick.waveform_id.station_code mstation = pick.waveform_id.station_code
mstation_ext = mstation + '_' mstation_ext = mstation + '_'
for mpick in arrivals_copy: for mpick in arrivals_copy:
phase = identifyPhase(loopIdentifyPhase(Pick.phase_hint)) phase = identifyPhase(loopIdentifyPhase(pick.phase_hint)) # MP MP catch if this fails?
if phase == 'P': if ((mpick.waveform_id.station_code == mstation) or
if ((mpick.waveform_id.station_code == mstation) or (mpick.waveform_id.station_code == mstation_ext)) and \
(mpick.waveform_id.station_code == mstation_ext)) and \ (mpick.method_id.id.split('/')[1] == 'auto') and \
(mpick.method_id.split('/')[1] == 'auto') and \ (mpick.time_errors['uncertainty'] <= errors[phase][3]):
(mpick.time_errors['uncertainty'] <= ErrorsP[3]): del mpick
del mpick break
break
elif phase == 'S':
if ((mpick.waveform_id.station_code == mstation) or
(mpick.waveform_id.station_code == mstation_ext)) and \
(mpick.method_id.split('/')[1] == 'auto') and \
(mpick.time_errors['uncertainty'] <= ErrorsS[3]):
del mpick
break
lendiff = len(arrivals) - len(arrivals_copy) lendiff = len(arrivals) - len(arrivals_copy)
if lendiff != 0: if lendiff != 0:
print("Found manual as well as automatic picks, prefered the {} manual ones!".format(lendiff)) print("Found manual as well as automatic picks, prefered the {} manual ones!".format(lendiff))
for Pick in arrivals_copy: for pick in arrivals_copy:
phase = identifyPhase(loopIdentifyPhase(Pick.phase_hint)) phase = identifyPhase(loopIdentifyPhase(pick.phase_hint))
if phase == 'P': uncertainty = pick.time_errors.uncertainty
Pqual = get_quality_class(Pick.time_errors.uncertainty, ErrorsP) if not uncertainty:
if Pqual == 0: if verbosity > 0:
Pw0.append(Pick.time_errors.uncertainty) print('No uncertainty, pick {} invalid!'.format(pick.method_id.id))
elif Pqual == 1: continue
Pw1.append(Pick.time_errors.uncertainty) # check P/S phase
elif Pqual == 2: if phase not in phases:
Pw2.append(Pick.time_errors.uncertainty)
elif Pqual == 3:
Pw3.append(Pick.time_errors.uncertainty)
elif Pqual == 4:
Pw4.append(Pick.time_errors.uncertainty)
elif phase == 'S':
Squal = get_quality_class(Pick.time_errors.uncertainty, ErrorsS)
if Squal == 0:
Sw0.append(Pick.time_errors.uncertainty)
elif Squal == 1:
Sw1.append(Pick.time_errors.uncertainty)
elif Squal == 2:
Sw2.append(Pick.time_errors.uncertainty)
elif Squal == 3:
Sw3.append(Pick.time_errors.uncertainty)
elif Squal == 4:
Sw4.append(Pick.time_errors.uncertainty)
else:
print("Phase hint not defined for picking!") print("Phase hint not defined for picking!")
pass continue
qual = get_quality_class(uncertainty, errors[phase])
weights[phase][qual].append(uncertainty)
if plotflag == 0: if plotflag == 0:
Punc = [Pw0, Pw1, Pw2, Pw3, Pw4] p_unc = [weights['P'][weight_id] for weight_id in weight_ids]
Sunc = [Sw0, Sw1, Sw2, Sw3, Sw4] s_unc = [weights['S'][weight_id] for weight_id in weight_ids]
return Punc, Sunc return p_unc, s_unc
else: else:
if not figure:
fig = plt.figure()
ax = fig.add_subplot(111)
# get percentage of weights # get percentage of weights
numPweights = np.sum([len(Pw0), len(Pw1), len(Pw2), len(Pw3), len(Pw4)]) listP, numPweights = calc_weight_perc(weights['P'], weight_ids)
numSweights = np.sum([len(Sw0), len(Sw1), len(Sw2), len(Sw3), len(Sw4)]) listS, numSweights = calc_weight_perc(weights['S'], weight_ids)
if len(Pw0) > 0:
P0perc = 100 / numPweights * len(Pw0)
else:
P0perc = 0
if len(Pw1) > 0:
P1perc = 100 / numPweights * len(Pw1)
else:
P1perc = 0
if len(Pw2) > 0:
P2perc = 100 / numPweights * len(Pw2)
else:
P2perc = 0
if len(Pw3) > 0:
P3perc = 100 / numPweights * len(Pw3)
else:
P3perc = 0
if len(Pw4) > 0:
P4perc = 100 / numPweights * len(Pw4)
else:
P4perc = 0
if len(Sw0) > 0:
S0perc = 100 / numSweights * len(Sw0)
else:
S0perc = 0
if len(Sw1) > 0:
S1perc = 100 / numSweights * len(Sw1)
else:
S1perc = 0
if len(Sw2) > 0:
S2perc = 100 / numSweights * len(Sw2)
else:
S2perc = 0
if len(Sw3) > 0:
S3perc = 100 / numSweights * len(Sw3)
else:
S3perc = 0
if len(Sw4) > 0:
S4perc = 100 / numSweights * len(Sw4)
else:
S4perc = 0
weights = ('0', '1', '2', '3', '4') y_pos = np.arange(len(weight_ids))
y_pos = np.arange(len(weights))
width = 0.34 width = 0.34
plt.bar(y_pos - width, [P0perc, P1perc, P2perc, P3perc, P4perc], width, color='black') ax.bar(y_pos - width, listP, width, color='black')
plt.bar(y_pos, [S0perc, S1perc, S2perc, S3perc, S4perc], width, color='red') ax.bar(y_pos, listS, width, color='red')
plt.ylabel('%') ax.set_ylabel('%')
plt.xticks(y_pos, weights) ax.set_xticks(y_pos, weight_ids)
plt.xlim([-0.5, 4.5]) ax.set_xlim([-0.5, 4.5])
plt.xlabel('Qualities') ax.set_xlabel('Qualities')
plt.title('{0} P-Qualities, {1} S-Qualities'.format(numPweights, numSweights)) ax.set_title('{0} P-Qualities, {1} S-Qualities'.format(numPweights, numSweights))
plt.show()
if not figure:
fig.show()
return listP, listS
+4 -3
View File
@@ -4,11 +4,12 @@
import glob import glob
import os import os
import subprocess import subprocess
from obspy import read_events from obspy import read_events
from pylot.core.io.phases import writephases from pylot.core.io.phases import writephases
from pylot.core.util.utils import getPatternLine, runProgram
from pylot.core.util.gui import which from pylot.core.util.gui import which
from pylot.core.util.utils import getPatternLine, runProgram
from pylot.core.util.version import get_git_version as _getVersionString from pylot.core.util.version import get_git_version as _getVersionString
__version__ = _getVersionString() __version__ = _getVersionString()
@@ -81,8 +82,8 @@ def locate(fnin, parameter=None):
:param fnin: external program name :param fnin: external program name
:return: None :return: None
""" """
exe_path = which('NLLoc', parameter) exe_path = os.path.join(parameter['nllocbin'], 'NLLoc')
if exe_path is None: if not os.path.isfile(exe_path):
raise NLLocError('NonLinLoc executable not found; check your ' raise NLLocError('NonLinLoc executable not found; check your '
'environment variables') 'environment variables')
+135 -85
View File
@@ -9,21 +9,21 @@ function conglomerate utils.
:author: MAGS2 EP3 working group / Ludger Kueperkoch :author: MAGS2 EP3 working group / Ludger Kueperkoch
""" """
import copy import copy
import traceback
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
import numpy as np import numpy as np
import traceback from obspy import Trace
from obspy.taup import TauPyModel from obspy.taup import TauPyModel
from pylot.core.pick.charfuns import CharacteristicFunction from pylot.core.pick.charfuns import CharacteristicFunction
from pylot.core.pick.charfuns import HOScf, AICcf, ARZcf, ARHcf, AR3Ccf from pylot.core.pick.charfuns import HOScf, AICcf, ARZcf, ARHcf, AR3Ccf
from pylot.core.pick.picker import AICPicker, PragPicker from pylot.core.pick.picker import AICPicker, PragPicker
from pylot.core.pick.utils import checksignallength, checkZ4S, earllatepicker, \ from pylot.core.pick.utils import checksignallength, checkZ4S, earllatepicker, \
getSNR, fmpicker, checkPonsets, wadaticheck, get_pickparams, get_quality_class getSNR, fmpicker, checkPonsets, wadaticheck, get_quality_class
from pylot.core.util.utils import getPatternLine, gen_Pool,\ from pylot.core.util.utils import getPatternLine, gen_Pool, \
get_Bool, identifyPhaseID, get_None, correct_iplot get_bool, identifyPhaseID, get_None, correct_iplot
from obspy.taup import TauPyModel
from obspy import Trace
def autopickevent(data, param, iplot=0, fig_dict=None, fig_dict_wadatijack=None, ncores=0, metadata=None, origin=None): def autopickevent(data, param, iplot=0, fig_dict=None, fig_dict_wadatijack=None, ncores=0, metadata=None, origin=None):
""" """
@@ -182,25 +182,25 @@ class PickingResults(dict):
# TODO What are those? # TODO What are those?
self.w0 = None self.w0 = None
self.fc = None self.fc = None
self.Ao = None # Wood-Anderson peak-to-peak amplitude self.Ao = None # Wood-Anderson peak-to-peak amplitude
# Station information # Station information
self.network = None self.network = None
self.channel = None self.channel = None
# pick information # pick information
self.picker = 'auto' # type of pick self.picker = 'auto' # type of pick
self.marked = [] self.marked = []
# pick results # pick results
self.epp = None # earliest possible pick self.epp = None # earliest possible pick
self.mpp = None # most likely onset self.mpp = None # most likely onset
self.lpp = None # latest possible pick self.lpp = None # latest possible pick
self.fm = 'N' # first motion polarity, can be set to 'U' (Up) or 'D' (Down) self.fm = 'N' # first motion polarity, can be set to 'U' (Up) or 'D' (Down)
self.snr = None # signal-to-noise ratio of onset self.snr = None # signal-to-noise ratio of onset
self.snrdb = None # signal-to-noise ratio of onset [dB] self.snrdb = None # signal-to-noise ratio of onset [dB]
self.spe = None # symmetrized picking error self.spe = None # symmetrized picking error
self.weight = 4 # weight of onset self.weight = 4 # weight of onset
# to correctly provide dot access to dictionary attributes, all attribute access of the class is forwarded to the # to correctly provide dot access to dictionary attributes, all attribute access of the class is forwarded to the
# dictionary # dictionary
@@ -335,9 +335,10 @@ class AutopickStation(object):
""" """
waveform_data = {} waveform_data = {}
for key in self.channelorder: for key in self.channelorder:
waveform_data[key] = self.wfstream.select(component=key) # try ZNE first waveform_data[key] = self.wfstream.select(component=key) # try ZNE first
if len(waveform_data[key]) == 0: if len(waveform_data[key]) == 0:
waveform_data[key] = self.wfstream.select(component=str(self.channelorder[key])) # use 123 as second option waveform_data[key] = self.wfstream.select(
component=str(self.channelorder[key])) # use 123 as second option
return waveform_data['Z'], waveform_data['N'], waveform_data['E'] return waveform_data['Z'], waveform_data['N'], waveform_data['E']
def get_traces_from_streams(self): def get_traces_from_streams(self):
@@ -428,7 +429,7 @@ class AutopickStation(object):
if station_coords is None: if station_coords is None:
exit_taupy() exit_taupy()
raise AttributeError('Warning: Could not find station in metadata') raise AttributeError('Warning: Could not find station in metadata')
# TODO raise when metadata.get_coordinates returns None # TODO: raise when metadata.get_coordinates returns None
source_origin = origin[0] source_origin = origin[0]
model = TauPyModel(taup_model) model = TauPyModel(taup_model)
taup_phases = self.pickparams['taup_phases'] taup_phases = self.pickparams['taup_phases']
@@ -470,11 +471,13 @@ class AutopickStation(object):
"""If taupy failed to calculate theoretical starttimes, picking continues. """If taupy failed to calculate theoretical starttimes, picking continues.
For this a clean exit is required, since the P starttime is no longer relative to the theoretic onset but For this a clean exit is required, since the P starttime is no longer relative to the theoretic onset but
to the vertical trace starttime, eg. it can't be < 0.""" to the vertical trace starttime, eg. it can't be < 0."""
# TODO here the pickparams is modified, instead of a copy
if self.pickparams["pstart"] < 0: if self.pickparams["pstart"] < 0:
# TODO here the pickparams is modified, instead of a copy
self.pickparams["pstart"] = 0 self.pickparams["pstart"] = 0
if self.pickparams["sstart"] < 0:
self.pickparams["sstart"] = 0
if self.pickparams["use_taup"] is False: if get_bool(self.pickparams["use_taup"]) is False:
# correct user mistake where a relative cuttime is selected (pstart < 0) but use of taupy is disabled/ has # correct user mistake where a relative cuttime is selected (pstart < 0) but use of taupy is disabled/ has
# not the required parameters # not the required parameters
exit_taupy() exit_taupy()
@@ -498,6 +501,20 @@ class AutopickStation(object):
self.pickparams["pstart"] = max(self.pickparams["pstart"], 0) self.pickparams["pstart"] = max(self.pickparams["pstart"], 0)
self.pickparams["pstop"] = min(self.pickparams["pstop"], len(self.ztrace) * self.ztrace.stats.delta) self.pickparams["pstop"] = min(self.pickparams["pstop"], len(self.ztrace) * self.ztrace.stats.delta)
if self.horizontal_traces_exist():
# for the two horizontal components take earliest and latest time to make sure that the s onset is not clipped
# if start and endtime of horizontal traces differ, the s windowsize will automatically increase
trace_s_start = min([self.etrace.stats.starttime, self.ntrace.stats.starttime])
# modifiy sstart and sstop relative to estimated first S arrival (relative to station time axis)
self.pickparams["sstart"] += (self.origin[0].time + estFirstS) - trace_s_start
self.pickparams["sstop"] += (self.origin[0].time + estFirstS) - trace_s_start
print('autopick: CF calculation times respectively:'
' sstart: {} s, sstop: {} s'.format(self.pickparams["sstart"], self.pickparams["sstop"]))
# make sure pstart and pstop are inside the starttime/endtime of horizontal traces
self.pickparams["sstart"] = max(self.pickparams["sstart"], 0)
self.pickparams["sstop"] = min(self.pickparams["sstop"], len(self.ntrace) * self.ntrace.stats.delta,
len(self.etrace) * self.etrace.stats.delta)
def autopickstation(self): def autopickstation(self):
""" """
Main function of autopickstation, which calculates P and S picks and returns them in a dictionary. Main function of autopickstation, which calculates P and S picks and returns them in a dictionary.
@@ -507,6 +524,17 @@ class AutopickStation(object):
station's value is the station name on which the picks were calculated. station's value is the station name on which the picks were calculated.
:rtype: dict :rtype: dict
""" """
if get_bool(self.pickparams['use_taup']) is True and self.origin is not None:
try:
# modify pstart, pstop, sstart, sstop to be around theoretical onset if taupy should be used,
# else do nothing
self.modify_starttimes_taupy()
except AttributeError as ae:
print(ae)
except MissingTraceException as mte:
print(mte)
try: try:
self.pick_p_phase() self.pick_p_phase()
except MissingTraceException as mte: except MissingTraceException as mte:
@@ -514,17 +542,19 @@ class AutopickStation(object):
except PickingFailedException as pfe: except PickingFailedException as pfe:
print(pfe) print(pfe)
if self.horizontal_traces_exist() and self.p_results.weight is not None and self.p_results.weight < 4: if self.horizontal_traces_exist():
try: if (self.p_results.weight is not None and self.p_results.weight < 4) or \
self.pick_s_phase() get_bool(self.pickparams.get('use_taup')):
except MissingTraceException as mte: try:
print(mte) self.pick_s_phase()
except PickingFailedException as pfe: except MissingTraceException as mte:
print(pfe) print(mte)
except PickingFailedException as pfe:
print(pfe)
self.plot_pick_results() self.plot_pick_results()
self.finish_picking() self.finish_picking()
return [{'P': self.p_results, 'S':self.s_results}, self.ztrace.stats.station] return [{'P': self.p_results, 'S': self.s_results}, self.ztrace.stats.station]
def finish_picking(self): def finish_picking(self):
@@ -573,7 +603,7 @@ class AutopickStation(object):
self.s_results.channel = self.etrace.stats.channel self.s_results.channel = self.etrace.stats.channel
self.s_results.network = self.etrace.stats.network self.s_results.network = self.etrace.stats.network
self.s_results.fm = None # override default value 'N' self.s_results.fm = None # override default value 'N'
def plot_pick_results(self): def plot_pick_results(self):
if self.iplot > 0: if self.iplot > 0:
@@ -588,12 +618,14 @@ class AutopickStation(object):
plt_flag = 0 plt_flag = 0
fig._tight = True fig._tight = True
ax1 = fig.add_subplot(311) ax1 = fig.add_subplot(311)
tdata = np.linspace(start=0, stop=self.ztrace.stats.endtime-self.ztrace.stats.starttime, num=self.ztrace.stats.npts) tdata = np.linspace(start=0, stop=self.ztrace.stats.endtime - self.ztrace.stats.starttime,
num=self.ztrace.stats.npts)
# plot tapered trace filtered with bpz2 filter settings # plot tapered trace filtered with bpz2 filter settings
ax1.plot(tdata, self.tr_filt_z_bpz2.data/max(self.tr_filt_z_bpz2.data), color=linecolor, linewidth=0.7, label='Data') ax1.plot(tdata, self.tr_filt_z_bpz2.data / max(self.tr_filt_z_bpz2.data), color=linecolor, linewidth=0.7,
label='Data')
if self.p_results.weight < 4: if self.p_results.weight < 4:
# plot CF of initial onset (HOScf or ARZcf) # plot CF of initial onset (HOScf or ARZcf)
ax1.plot(self.cf1.getTimeArray(), self.cf1.getCF()/max(self.cf1.getCF()), 'b', label='CF1') ax1.plot(self.cf1.getTimeArray(), self.cf1.getCF() / max(self.cf1.getCF()), 'b', label='CF1')
if self.p_data.p_aic_plot_flag == 1: if self.p_data.p_aic_plot_flag == 1:
aicpick = self.p_data.aicpick aicpick = self.p_data.aicpick
refPpick = self.p_data.refPpick refPpick = self.p_data.refPpick
@@ -631,35 +663,41 @@ class AutopickStation(object):
if self.horizontal_traces_exist() and self.s_data.Sflag == 1: if self.horizontal_traces_exist() and self.s_data.Sflag == 1:
# plot E trace # plot E trace
ax2 = fig.add_subplot(3, 1, 2, sharex=ax1) ax2 = fig.add_subplot(3, 1, 2, sharex=ax1)
th1data = np.linspace(0, self.etrace.stats.endtime-self.etrace.stats.starttime, self.etrace.stats.npts) th1data = np.linspace(0, self.etrace.stats.endtime - self.etrace.stats.starttime,
self.etrace.stats.npts)
# plot filtered and tapered waveform # plot filtered and tapered waveform
ax2.plot(th1data, self.etrace.data / max(self.etrace.data), color=linecolor, linewidth=0.7, label='Data') ax2.plot(th1data, self.etrace.data / max(self.etrace.data), color=linecolor, linewidth=0.7,
label='Data')
if self.p_results.weight < 4: if self.p_results.weight < 4:
# plot initial CF (ARHcf or AR3Ccf) # plot initial CF (ARHcf or AR3Ccf)
ax2.plot(self.arhcf1.getTimeArray(), self.arhcf1.getCF() / max(self.arhcf1.getCF()), 'b', label='CF1') ax2.plot(self.arhcf1.getTimeArray(), self.arhcf1.getCF() / max(self.arhcf1.getCF()), 'b',
label='CF1')
if self.s_data.aicSflag == 1 and self.s_results.weight <= 4: if self.s_data.aicSflag == 1 and self.s_results.weight <= 4:
aicarhpick = self.aicarhpick aicarhpick = self.aicarhpick
refSpick = self.refSpick refSpick = self.refSpick
# plot second cf, used for determing precise onset (ARHcf or AR3Ccf) # plot second cf, used for determing precise onset (ARHcf or AR3Ccf)
ax2.plot(self.arhcf2.getTimeArray(), self.arhcf2.getCF() / max(self.arhcf2.getCF()), 'm', label='CF2') ax2.plot(self.arhcf2.getTimeArray(), self.arhcf2.getCF() / max(self.arhcf2.getCF()), 'm',
label='CF2')
# plot preliminary onset time, calculated from CF1 # plot preliminary onset time, calculated from CF1
ax2.plot([aicarhpick.getpick(), aicarhpick.getpick()], [-1, 1], 'g', label='Initial S Onset') ax2.plot([aicarhpick.getpick(), aicarhpick.getpick()], [-1, 1], 'g', label='Initial S Onset')
ax2.plot([aicarhpick.getpick() - 0.5, aicarhpick.getpick() + 0.5], [1, 1], 'g') ax2.plot([aicarhpick.getpick() - 0.5, aicarhpick.getpick() + 0.5], [1, 1], 'g')
ax2.plot([aicarhpick.getpick() - 0.5, aicarhpick.getpick() + 0.5], [-1, -1], 'g') ax2.plot([aicarhpick.getpick() - 0.5, aicarhpick.getpick() + 0.5], [-1, -1], 'g')
# plot precise onset time, calculated from CF2 # plot precise onset time, calculated from CF2
ax2.plot([refSpick.getpick(), refSpick.getpick()], [-1.3, 1.3], 'g', linewidth=2, label='Final S Pick') ax2.plot([refSpick.getpick(), refSpick.getpick()], [-1.3, 1.3], 'g', linewidth=2,
label='Final S Pick')
ax2.plot([refSpick.getpick() - 0.5, refSpick.getpick() + 0.5], [1.3, 1.3], 'g', linewidth=2) ax2.plot([refSpick.getpick() - 0.5, refSpick.getpick() + 0.5], [1.3, 1.3], 'g', linewidth=2)
ax2.plot([refSpick.getpick() - 0.5, refSpick.getpick() + 0.5], [-1.3, -1.3], 'g', linewidth=2) ax2.plot([refSpick.getpick() - 0.5, refSpick.getpick() + 0.5], [-1.3, -1.3], 'g', linewidth=2)
ax2.plot([self.s_results.lpp, self.s_results.lpp], [-1.1, 1.1], 'g--', label='lpp') ax2.plot([self.s_results.lpp, self.s_results.lpp], [-1.1, 1.1], 'g--', label='lpp')
ax2.plot([self.s_results.epp, self.s_results.epp], [-1.1, 1.1], 'g--', label='epp') ax2.plot([self.s_results.epp, self.s_results.epp], [-1.1, 1.1], 'g--', label='epp')
title = '{channel}, S weight={sweight}, SNR={snr:7.2}, SNR[dB]={snrdb:7.2}' title = '{channel}, S weight={sweight}, SNR={snr:7.2}, SNR[dB]={snrdb:7.2}'
ax2.set_title(title.format(channel=self.etrace.stats.channel, ax2.set_title(title.format(channel=str(self.etrace.stats.channel),
sweight=self.s_results.weight, sweight=str(self.s_results.weight),
snr=self.s_results.snr, snr=str(self.s_results.snr),
snrdb=self.s_results.snrdb)) snrdb=str(self.s_results.snrdb)))
else: else:
title = '{channel}, S weight={sweight}, SNR=None, SNR[dB]=None' title = '{channel}, S weight={sweight}, SNR=None, SNR[dB]=None'
ax2.set_title(title.format(channel=self.etrace.stats.channel, sweight=self.s_results.weight)) ax2.set_title(title.format(channel=str(self.etrace.stats.channel),
sweight=str(self.s_results.weight)))
ax2.legend(loc=1) ax2.legend(loc=1)
ax2.set_yticks([]) ax2.set_yticks([])
ax2.set_ylim([-1.5, 1.5]) ax2.set_ylim([-1.5, 1.5])
@@ -667,15 +705,19 @@ class AutopickStation(object):
# plot N trace # plot N trace
ax3 = fig.add_subplot(3, 1, 3, sharex=ax1) ax3 = fig.add_subplot(3, 1, 3, sharex=ax1)
th2data= np.linspace(0, self.ntrace.stats.endtime-self.ntrace.stats.starttime, self.ntrace.stats.npts) th2data = np.linspace(0, self.ntrace.stats.endtime - self.ntrace.stats.starttime,
self.ntrace.stats.npts)
# plot trace # plot trace
ax3.plot(th2data, self.ntrace.data / max(self.ntrace.data), color=linecolor, linewidth=0.7, label='Data') ax3.plot(th2data, self.ntrace.data / max(self.ntrace.data), color=linecolor, linewidth=0.7,
label='Data')
if self.p_results.weight < 4: if self.p_results.weight < 4:
p22, = ax3.plot(self.arhcf1.getTimeArray(), self.arhcf1.getCF() / max(self.arhcf1.getCF()), 'b', label='CF1') p22, = ax3.plot(self.arhcf1.getTimeArray(), self.arhcf1.getCF() / max(self.arhcf1.getCF()), 'b',
label='CF1')
if self.s_data.aicSflag == 1: if self.s_data.aicSflag == 1:
aicarhpick = self.aicarhpick aicarhpick = self.aicarhpick
refSpick = self.refSpick refSpick = self.refSpick
ax3.plot(self.arhcf2.getTimeArray(), self.arhcf2.getCF() / max(self.arhcf2.getCF()), 'm', label='CF2') ax3.plot(self.arhcf2.getTimeArray(), self.arhcf2.getCF() / max(self.arhcf2.getCF()), 'm',
label='CF2')
ax3.plot([aicarhpick.getpick(), aicarhpick.getpick()], [-1, 1], 'g', label='Initial S Onset') ax3.plot([aicarhpick.getpick(), aicarhpick.getpick()], [-1, 1], 'g', label='Initial S Onset')
ax3.plot([aicarhpick.getpick() - 0.5, aicarhpick.getpick() + 0.5], [1, 1], 'g') ax3.plot([aicarhpick.getpick() - 0.5, aicarhpick.getpick() + 0.5], [1, 1], 'g')
ax3.plot([aicarhpick.getpick() - 0.5, aicarhpick.getpick() + 0.5], [-1, -1], 'g') ax3.plot([aicarhpick.getpick() - 0.5, aicarhpick.getpick() + 0.5], [-1, -1], 'g')
@@ -716,7 +758,8 @@ class AutopickStation(object):
if aicpick.getpick() is None: if aicpick.getpick() is None:
msg = "Bad initial (AIC) P-pick, skipping this onset!\nAIC-SNR={0}, AIC-Slope={1}counts/s\n " \ msg = "Bad initial (AIC) P-pick, skipping this onset!\nAIC-SNR={0}, AIC-Slope={1}counts/s\n " \
"(min. AIC-SNR={2}, min. AIC-Slope={3}counts/s)" "(min. AIC-SNR={2}, min. AIC-Slope={3}counts/s)"
msg = msg.format(aicpick.getSNR(), aicpick.getSlope(), self.pickparams["minAICPSNR"], self.pickparams["minAICPslope"]) msg = msg.format(aicpick.getSNR(), aicpick.getSlope(), self.pickparams["minAICPSNR"],
self.pickparams["minAICPslope"])
self.vprint(msg) self.vprint(msg)
return 0 return 0
# Quality check initial pick with minimum signal length # Quality check initial pick with minimum signal length
@@ -726,14 +769,16 @@ class AutopickStation(object):
if len(self.nstream) == 0 or len(self.estream) == 0: if len(self.nstream) == 0 or len(self.estream) == 0:
msg = 'One or more horizontal component(s) missing!\n' \ msg = 'One or more horizontal component(s) missing!\n' \
'Signal length only checked on vertical component!\n' \ 'Signal length only checked on vertical component!\n' \
'Decreasing minsiglengh from {0} to {1}'\ 'Decreasing minsiglengh from {0} to {1}' \
.format(minsiglength, minsiglength / 2) .format(minsiglength, minsiglength / 2)
self.vprint(msg) self.vprint(msg)
minsiglength = minsiglength / 2 minsiglength = minsiglength / 2
else: else:
# filter, taper other traces as well since signal length is compared on all traces # filter, taper other traces as well since signal length is compared on all traces
trH1_filt, _ = self.prepare_wfstream(self.estream, freqmin=self.pickparams["bph1"][0], freqmax=self.pickparams["bph1"][1]) trH1_filt, _ = self.prepare_wfstream(self.estream, freqmin=self.pickparams["bph1"][0],
trH2_filt, _ = self.prepare_wfstream(self.nstream, freqmin=self.pickparams["bph1"][0], freqmax=self.pickparams["bph1"][1]) freqmax=self.pickparams["bph1"][1])
trH2_filt, _ = self.prepare_wfstream(self.nstream, freqmin=self.pickparams["bph1"][0],
freqmax=self.pickparams["bph1"][1])
zne += trH1_filt zne += trH1_filt
zne += trH2_filt zne += trH2_filt
minsiglength = minsiglength minsiglength = minsiglength
@@ -780,15 +825,6 @@ class AutopickStation(object):
# save filtered trace in instance for later plotting # save filtered trace in instance for later plotting
self.tr_filt_z_bpz2 = tr_filt self.tr_filt_z_bpz2 = tr_filt
if get_Bool(self.pickparams['use_taup']) is True and self.origin is not None:
try:
# modify pstart, pstop to be around theoretical onset if taupy should be used, else does nothing
self.modify_starttimes_taupy()
except AttributeError as ae:
print(ae)
except MissingTraceException as mte:
print(mte)
Lc = self.pickparams['pstop'] - self.pickparams['pstart'] Lc = self.pickparams['pstop'] - self.pickparams['pstart']
Lwf = self.ztrace.stats.endtime - self.ztrace.stats.starttime Lwf = self.ztrace.stats.endtime - self.ztrace.stats.starttime
@@ -819,15 +855,18 @@ class AutopickStation(object):
# get preliminary onset time from AIC-CF # get preliminary onset time from AIC-CF
self.set_current_figure('aicFig') self.set_current_figure('aicFig')
aicpick = AICPicker(aiccf, self.pickparams["tsnrz"], self.pickparams["pickwinP"], self.iplot, aicpick = AICPicker(aiccf, self.pickparams["tsnrz"], self.pickparams["pickwinP"], self.iplot,
Tsmooth=self.pickparams["aictsmooth"], fig=self.current_figure, linecolor=self.current_linecolor) Tsmooth=self.pickparams["aictsmooth"], fig=self.current_figure,
linecolor=self.current_linecolor)
# save aicpick for plotting later # save aicpick for plotting later
self.p_data.aicpick = aicpick self.p_data.aicpick = aicpick
# add pstart and pstop to aic plot # add pstart and pstop to aic plot
if self.current_figure: if self.current_figure:
# TODO remove plotting from picking, make own plot function # TODO remove plotting from picking, make own plot function
for ax in self.current_figure.axes: for ax in self.current_figure.axes:
ax.vlines(self.pickparams["pstart"], ax.get_ylim()[0], ax.get_ylim()[1], color='c', linestyles='dashed', label='P start') ax.vlines(self.pickparams["pstart"], ax.get_ylim()[0], ax.get_ylim()[1], color='c', linestyles='dashed',
ax.vlines(self.pickparams["pstop"], ax.get_ylim()[0], ax.get_ylim()[1], color='c', linestyles='dashed', label='P stop') label='P start')
ax.vlines(self.pickparams["pstop"], ax.get_ylim()[0], ax.get_ylim()[1], color='c', linestyles='dashed',
label='P stop')
ax.legend(loc=1) ax.legend(loc=1)
Pflag = self._pick_p_quality_control(aicpick, z_copy, tr_filt) Pflag = self._pick_p_quality_control(aicpick, z_copy, tr_filt)
@@ -841,7 +880,8 @@ class AutopickStation(object):
error_msg = 'AIC P onset slope to small: got {}, min {}'.format(slope, self.pickparams["minAICPslope"]) error_msg = 'AIC P onset slope to small: got {}, min {}'.format(slope, self.pickparams["minAICPslope"])
raise PickingFailedException(error_msg) raise PickingFailedException(error_msg)
if aicpick.getSNR() < self.pickparams["minAICPSNR"]: if aicpick.getSNR() < self.pickparams["minAICPSNR"]:
error_msg = 'AIC P onset SNR to small: got {}, min {}'.format(aicpick.getSNR(), self.pickparams["minAICPSNR"]) error_msg = 'AIC P onset SNR to small: got {}, min {}'.format(aicpick.getSNR(),
self.pickparams["minAICPSNR"])
raise PickingFailedException(error_msg) raise PickingFailedException(error_msg)
self.p_data.p_aic_plot_flag = 1 self.p_data.p_aic_plot_flag = 1
@@ -849,7 +889,8 @@ class AutopickStation(object):
'autopickstation: re-filtering vertical trace...'.format(aicpick.getSlope(), aicpick.getSNR()) 'autopickstation: re-filtering vertical trace...'.format(aicpick.getSlope(), aicpick.getSNR())
self.vprint(msg) self.vprint(msg)
# refilter waveform with larger bandpass # refilter waveform with larger bandpass
tr_filt, z_copy = self.prepare_wfstream(self.zstream, freqmin=self.pickparams["bpz2"][0], freqmax=self.pickparams["bpz2"][1]) tr_filt, z_copy = self.prepare_wfstream(self.zstream, freqmin=self.pickparams["bpz2"][0],
freqmax=self.pickparams["bpz2"][1])
# save filtered trace in instance for later plotting # save filtered trace in instance for later plotting
self.tr_filt_z_bpz2 = tr_filt self.tr_filt_z_bpz2 = tr_filt
# determine new times around initial onset # determine new times around initial onset
@@ -861,25 +902,29 @@ class AutopickStation(object):
else: else:
self.cf2 = None self.cf2 = None
assert isinstance(self.cf2, CharacteristicFunction), 'cf2 is not set correctly: maybe the algorithm name () is ' \ assert isinstance(self.cf2, CharacteristicFunction), 'cf2 is not set correctly: maybe the algorithm name () is ' \
'corrupted'.format(self.pickparams["algoP"]) 'corrupted'.format(self.pickparams["algoP"])
self.set_current_figure('refPpick') self.set_current_figure('refPpick')
# get refined onset time from CF2 # get refined onset time from CF2
refPpick = PragPicker(self.cf2, self.pickparams["tsnrz"], self.pickparams["pickwinP"], self.iplot, self.pickparams["ausP"], refPpick = PragPicker(self.cf2, self.pickparams["tsnrz"], self.pickparams["pickwinP"], self.iplot,
self.pickparams["tsmoothP"], aicpick.getpick(), self.current_figure, self.current_linecolor) self.pickparams["ausP"],
self.pickparams["tsmoothP"], aicpick.getpick(), self.current_figure,
self.current_linecolor)
# save PragPicker result for plotting # save PragPicker result for plotting
self.p_data.refPpick = refPpick self.p_data.refPpick = refPpick
self.p_results.mpp = refPpick.getpick() self.p_results.mpp = refPpick.getpick()
if self.p_results.mpp is None: if self.p_results.mpp is None:
msg = 'Bad initial (AIC) P-pick, skipping this onset!\n AIC-SNR={}, AIC-Slope={}counts/s\n' \ msg = 'Bad initial (AIC) P-pick, skipping this onset!\n AIC-SNR={}, AIC-Slope={}counts/s\n' \
'(min. AIC-SNR={}, min. AIC-Slope={}counts/s)' '(min. AIC-SNR={}, min. AIC-Slope={}counts/s)'
msg.format(aicpick.getSNR(), aicpick.getSlope(), self.pickparams["minAICPSNR"], self.pickparams["minAICPslope"]) msg.format(aicpick.getSNR(), aicpick.getSlope(), self.pickparams["minAICPSNR"],
self.pickparams["minAICPslope"])
self.vprint(msg) self.vprint(msg)
self.s_data.Sflag = 0 self.s_data.Sflag = 0
raise PickingFailedException(msg) raise PickingFailedException(msg)
# quality assessment, get earliest/latest pick and symmetrized uncertainty # quality assessment, get earliest/latest pick and symmetrized uncertainty
#todo quality assessment in own function # todo quality assessment in own function
self.set_current_figure('el_Ppick') self.set_current_figure('el_Ppick')
elpicker_results = earllatepicker(z_copy, self.pickparams["nfacP"], self.pickparams["tsnrz"], self.p_results.mpp, elpicker_results = earllatepicker(z_copy, self.pickparams["nfacP"], self.pickparams["tsnrz"],
self.p_results.mpp,
self.iplot, fig=self.current_figure, linecolor=self.current_linecolor) self.iplot, fig=self.current_figure, linecolor=self.current_linecolor)
self.p_results.epp, self.p_results.lpp, self.p_results.spe = elpicker_results self.p_results.epp, self.p_results.lpp, self.p_results.spe = elpicker_results
snr_results = getSNR(z_copy, self.pickparams["tsnrz"], self.p_results.mpp) snr_results = getSNR(z_copy, self.pickparams["tsnrz"], self.p_results.mpp)
@@ -887,10 +932,11 @@ class AutopickStation(object):
# weight P-onset using symmetric error # weight P-onset using symmetric error
self.p_results.weight = get_quality_class(self.p_results.spe, self.pickparams["timeerrorsP"]) self.p_results.weight = get_quality_class(self.p_results.spe, self.pickparams["timeerrorsP"])
if self.p_results.weight <= self.pickparams["minfmweight"] and self.p_results.snr >= self.pickparams["minFMSNR"]: if self.p_results.weight <= self.pickparams["minfmweight"] and self.p_results.snr >= self.pickparams[
"minFMSNR"]:
# if SNR is high enough, try to determine first motion of onset # if SNR is high enough, try to determine first motion of onset
self.set_current_figure('fm_picker') self.set_current_figure('fm_picker')
self.p_results.fm = fmpicker(self.zstream, z_copy, self.pickparams["fmpickwin"], self.p_results.mpp, self.p_results.fm = fmpicker(self.zstream.copy(), z_copy, self.pickparams["fmpickwin"], self.p_results.mpp,
self.iplot, self.current_figure, self.current_linecolor) self.iplot, self.current_figure, self.current_linecolor)
msg = "autopickstation: P-weight: {}, SNR: {}, SNR[dB]: {}, Polarity: {}" msg = "autopickstation: P-weight: {}, SNR: {}, SNR[dB]: {}, Polarity: {}"
msg = msg.format(self.p_results.weight, self.p_results.snr, self.p_results.snrdb, self.p_results.fm) msg = msg.format(self.p_results.weight, self.p_results.snr, self.p_results.snrdb, self.p_results.fm)
@@ -960,7 +1006,7 @@ class AutopickStation(object):
trH1_filt, _ = self.prepare_wfstream(self.zstream, filter_freq_min, filter_freq_max) trH1_filt, _ = self.prepare_wfstream(self.zstream, filter_freq_min, filter_freq_max)
trH2_filt, _ = self.prepare_wfstream(self.estream, filter_freq_min, filter_freq_max) trH2_filt, _ = self.prepare_wfstream(self.estream, filter_freq_min, filter_freq_max)
trH3_filt, _ = self.prepare_wfstream(self.nstream, filter_freq_min, filter_freq_max) trH3_filt, _ = self.prepare_wfstream(self.nstream, filter_freq_min, filter_freq_max)
h_copy =self. hdat.copy() h_copy = self.hdat.copy()
h_copy[0].data = trH1_filt.data h_copy[0].data = trH1_filt.data
h_copy[1].data = trH2_filt.data h_copy[1].data = trH2_filt.data
h_copy[2].data = trH3_filt.data h_copy[2].data = trH3_filt.data
@@ -1102,9 +1148,11 @@ class AutopickStation(object):
''.format(self.s_results.weight, self.s_results.snr, self.s_results.snrdb)) ''.format(self.s_results.weight, self.s_results.snr, self.s_results.snrdb))
def pick_s_phase(self): def pick_s_phase(self):
if get_bool(self.pickparams.get('use_taup')) is True:
# determine time window for calculating CF after P onset cuttimesh = (self.pickparams.get('sstart'), self.pickparams.get('sstop'))
cuttimesh = self._calculate_cuttimes(type='S', iteration=1) else:
# determine time window for calculating CF after P onset
cuttimesh = self._calculate_cuttimes(type='S', iteration=1)
# calculate autoregressive CF # calculate autoregressive CF
self.arhcf1 = self._calculate_autoregressive_cf_s_pick(cuttimesh) self.arhcf1 = self._calculate_autoregressive_cf_s_pick(cuttimesh)
@@ -1115,7 +1163,8 @@ class AutopickStation(object):
# get preliminary onset time from AIC cf # get preliminary onset time from AIC cf
self.set_current_figure('aicARHfig') self.set_current_figure('aicARHfig')
aicarhpick = AICPicker(haiccf, self.pickparams["tsnrh"], self.pickparams["pickwinS"], self.iplot, aicarhpick = AICPicker(haiccf, self.pickparams["tsnrh"], self.pickparams["pickwinS"], self.iplot,
Tsmooth=self.pickparams["aictsmoothS"], fig=self.current_figure, linecolor=self.current_linecolor) Tsmooth=self.pickparams["aictsmoothS"], fig=self.current_figure,
linecolor=self.current_linecolor)
# save pick for later plotting # save pick for later plotting
self.aicarhpick = aicarhpick self.aicarhpick = aicarhpick
@@ -1126,8 +1175,10 @@ class AutopickStation(object):
# get refined onset time from CF2 # get refined onset time from CF2
self.set_current_figure('refSpick') self.set_current_figure('refSpick')
refSpick = PragPicker(arhcf2, self.pickparams["tsnrh"], self.pickparams["pickwinS"], self.iplot, self.pickparams["ausS"], refSpick = PragPicker(arhcf2, self.pickparams["tsnrh"], self.pickparams["pickwinS"], self.iplot,
self.pickparams["tsmoothS"], aicarhpick.getpick(), self.current_figure, self.current_linecolor) self.pickparams["ausS"],
self.pickparams["tsmoothS"], aicarhpick.getpick(), self.current_figure,
self.current_linecolor)
# save refSpick for later plotitng # save refSpick for later plotitng
self.refSpick = refSpick self.refSpick = refSpick
self.s_results.mpp = refSpick.getpick() self.s_results.mpp = refSpick.getpick()
@@ -1151,7 +1202,6 @@ class AutopickStation(object):
self.current_linecolor = plot_style['linecolor']['rgba_mpl'] self.current_linecolor = plot_style['linecolor']['rgba_mpl']
def autopickstation(wfstream, pickparam, verbose=False, iplot=0, fig_dict=None, metadata=None, origin=None): def autopickstation(wfstream, pickparam, verbose=False, iplot=0, fig_dict=None, metadata=None, origin=None):
""" """
Main function to calculate picks for the station. Main function to calculate picks for the station.
@@ -1239,11 +1289,11 @@ def iteratepicker(wf, NLLocfile, picks, badpicks, pickparameter, fig_dict=None):
print( print(
"iteratepicker: The following picking parameters have been modified for iterative picking:") "iteratepicker: The following picking parameters have been modified for iterative picking:")
print( print(
"pstart: %fs => %fs" % (pstart_old, pickparameter.get('pstart'))) "pstart: %fs => %fs" % (pstart_old, pickparameter.get('pstart')))
print( print(
"pstop: %fs => %fs" % (pstop_old, pickparameter.get('pstop'))) "pstop: %fs => %fs" % (pstop_old, pickparameter.get('pstop')))
print( print(
"sstop: %fs => %fs" % (sstop_old, pickparameter.get('sstop'))) "sstop: %fs => %fs" % (sstop_old, pickparameter.get('sstop')))
print("pickwinP: %fs => %fs" % ( print("pickwinP: %fs => %fs" % (
pickwinP_old, pickparameter.get('pickwinP'))) pickwinP_old, pickparameter.get('pickwinP')))
print("Precalcwin: %fs => %fs" % ( print("Precalcwin: %fs => %fs" % (
+31 -7
View File
@@ -16,7 +16,6 @@ autoregressive prediction: application ot local and regional distances, Geophys.
:author: MAGS2 EP3 working group :author: MAGS2 EP3 working group
""" """
import numpy as np import numpy as np
from scipy import signal from scipy import signal
from obspy.core import Stream from obspy.core import Stream
@@ -159,7 +158,7 @@ class CharacteristicFunction(object):
zz = self.orig_data.copy() zz = self.orig_data.copy()
z1 = zz[0].copy() z1 = zz[0].copy()
zz[0].data = z1.data[int(start):int(stop)] zz[0].data = z1.data[int(start):int(stop)]
if zz[0].stats.npts == 0: # cut times do not fit data length! if zz[0].stats.npts == 0: # cut times do not fit data length!
zz[0].data = z1.data # take entire data zz[0].data = z1.data # take entire data
data = zz data = zz
return data return data
@@ -259,7 +258,7 @@ class HOScf(CharacteristicFunction):
""" """
Function to calculate skewness (statistics of order 3) or kurtosis Function to calculate skewness (statistics of order 3) or kurtosis
(statistics of order 4), using one long moving window, as published (statistics of order 4), using one long moving window, as published
in Kueperkoch et al. (2010). in Kueperkoch et al. (2010), or order 2, i.e. STA/LTA.
:param data: data, time series (whether seismogram or CF) :param data: data, time series (whether seismogram or CF)
:type data: tuple :type data: tuple
:return: HOS cf :return: HOS cf
@@ -276,28 +275,47 @@ class HOScf(CharacteristicFunction):
elif self.getOrder() == 4: # this is kurtosis elif self.getOrder() == 4: # this is kurtosis
y = np.power(xnp, 4) y = np.power(xnp, 4)
y1 = np.power(xnp, 2) y1 = np.power(xnp, 2)
elif self.getOrder() == 2: # this is variance, used for STA/LTA processing
y = np.power(xnp, 2)
y1 = np.power(xnp, 2)
# Initialisation # Initialisation
# t2: long term moving window # t2: long term moving window
ilta = int(round(self.getTime2() / self.getIncrement())) ilta = int(round(self.getTime2() / self.getIncrement()))
ista = int(round((self.getTime2() / 10) / self.getIncrement())) # TODO: still hard coded!!
lta = y[0] lta = y[0]
lta1 = y1[0] lta1 = y1[0]
sta = y[0]
# moving windows # moving windows
LTA = np.zeros(len(xnp)) LTA = np.zeros(len(xnp))
STA = np.zeros(len(xnp))
for j in range(0, len(xnp)): for j in range(0, len(xnp)):
if j < 4: if j < 4:
LTA[j] = 0 LTA[j] = 0
STA[j] = 0
elif j <= ista:
lta = (y[j] + lta * (j - 1)) / j
if self.getOrder() == 2:
sta = (y[j] + sta * (j - 1)) / j
# elif j < 4:
elif j <= ilta: elif j <= ilta:
lta = (y[j] + lta * (j - 1)) / j lta = (y[j] + lta * (j - 1)) / j
lta1 = (y1[j] + lta1 * (j - 1)) / j lta1 = (y1[j] + lta1 * (j - 1)) / j
if self.getOrder() == 2:
sta = (y[j] - y[j - ista]) / ista + sta
else: else:
lta = (y[j] - y[j - ilta]) / ilta + lta lta = (y[j] - y[j - ilta]) / ilta + lta
lta1 = (y1[j] - y1[j - ilta]) / ilta + lta1 lta1 = (y1[j] - y1[j - ilta]) / ilta + lta1
if self.getOrder() == 2:
sta = (y[j] - y[j - ista]) / ista + sta
# define LTA # define LTA
if self.getOrder() == 3: if self.getOrder() == 3:
LTA[j] = lta / np.power(lta1, 1.5) LTA[j] = lta / np.power(lta1, 1.5)
elif self.getOrder() == 4: elif self.getOrder() == 4:
LTA[j] = lta / np.power(lta1, 2) LTA[j] = lta / np.power(lta1, 2)
else:
LTA[j] = lta
STA[j] = sta
# remove NaN's with first not-NaN-value, # remove NaN's with first not-NaN-value,
# so autopicker doesnt pick discontinuity at start of the trace # so autopicker doesnt pick discontinuity at start of the trace
@@ -306,14 +324,18 @@ class HOScf(CharacteristicFunction):
first = ind[0] first = ind[0]
LTA[:first] = LTA[first] LTA[:first] = LTA[first]
self.cf = LTA if self.getOrder() > 2:
self.cf = LTA
else: # order 2 means STA/LTA!
self.cf = STA / LTA
self.xcf = x self.xcf = x
class ARZcf(CharacteristicFunction): class ARZcf(CharacteristicFunction):
def __init__(self, data, cut, t1, t2, pickparams): def __init__(self, data, cut, t1, t2, pickparams):
super(ARZcf, self).__init__(data, cut, t1=t1, t2=t2, order=pickparams["Parorder"], fnoise=pickparams["addnoise"]) super(ARZcf, self).__init__(data, cut, t1=t1, t2=t2, order=pickparams["Parorder"],
fnoise=pickparams["addnoise"])
def calcCF(self, data): def calcCF(self, data):
""" """
@@ -448,7 +470,8 @@ class ARZcf(CharacteristicFunction):
class ARHcf(CharacteristicFunction): class ARHcf(CharacteristicFunction):
def __init__(self, data, cut, t1, t2, pickparams): def __init__(self, data, cut, t1, t2, pickparams):
super(ARHcf, self).__init__(data, cut, t1=t1, t2=t2, order=pickparams["Sarorder"], fnoise=pickparams["addnoise"]) super(ARHcf, self).__init__(data, cut, t1=t1, t2=t2, order=pickparams["Sarorder"],
fnoise=pickparams["addnoise"])
def calcCF(self, data): def calcCF(self, data):
""" """
@@ -600,7 +623,8 @@ class ARHcf(CharacteristicFunction):
class AR3Ccf(CharacteristicFunction): class AR3Ccf(CharacteristicFunction):
def __init__(self, data, cut, t1, t2, pickparams): def __init__(self, data, cut, t1, t2, pickparams):
super(AR3Ccf, self).__init__(data, cut, t1=t1, t2=t2, order=pickparams["Sarorder"], fnoise=pickparams["addnoise"]) super(AR3Ccf, self).__init__(data, cut, t1=t1, t2=t2, order=pickparams["Sarorder"],
fnoise=pickparams["addnoise"])
def calcCF(self, data): def calcCF(self, data):
""" """
+4 -2
View File
@@ -2,10 +2,11 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import copy import copy
import matplotlib.pyplot as plt
import numpy as np
import operator import operator
import os import os
import matplotlib.pyplot as plt
import numpy as np
from obspy.core import AttribDict from obspy.core import AttribDict
from pylot.core.util.pdf import ProbabilityDensityFunction from pylot.core.util.pdf import ProbabilityDensityFunction
@@ -400,6 +401,7 @@ class PDFstatistics(object):
This object can be used to get various statistic values from probability density functions. This object can be used to get various statistic values from probability density functions.
Takes a path as argument. Takes a path as argument.
""" """
# TODO: change root to datapath # TODO: change root to datapath
def __init__(self, directory): def __init__(self, directory):
+7 -6
View File
@@ -19,9 +19,10 @@ calculated after Diehl & Kissling (2009).
:author: MAGS2 EP3 working group / Ludger Kueperkoch :author: MAGS2 EP3 working group / Ludger Kueperkoch
""" """
import warnings
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
import numpy as np import numpy as np
import warnings
from scipy.signal import argrelmax, argrelmin from scipy.signal import argrelmax, argrelmin
from pylot.core.pick.charfuns import CharacteristicFunction from pylot.core.pick.charfuns import CharacteristicFunction
@@ -335,7 +336,7 @@ class AICPicker(AutoPicker):
self.slope = 1 / (len(dataslope) * self.Data[0].stats.delta) * (datafit[-1] - datafit[0]) self.slope = 1 / (len(dataslope) * self.Data[0].stats.delta) * (datafit[-1] - datafit[0])
# normalize slope to maximum of cf to make it unit independent # normalize slope to maximum of cf to make it unit independent
self.slope /= aicsmooth[iaicmax] self.slope /= aicsmooth[iaicmax]
except ValueError as e: except Exception as e:
print("AICPicker: Problems with data fitting! {}".format(e)) print("AICPicker: Problems with data fitting! {}".format(e))
else: else:
@@ -476,7 +477,7 @@ class PragPicker(AutoPicker):
cfpick_r = 0 cfpick_r = 0
cfpick_l = 0 cfpick_l = 0
lpickwindow = int(round(self.PickWindow / self.dt)) lpickwindow = int(round(self.PickWindow / self.dt))
#for i in range(max(np.insert(ipick, 0, 2)), min([ipick1 + lpickwindow + 1, len(self.cf) - 1])): # for i in range(max(np.insert(ipick, 0, 2)), min([ipick1 + lpickwindow + 1, len(self.cf) - 1])):
# # local minimum # # local minimum
# if self.cf[i + 1] > self.cf[i] <= self.cf[i - 1]: # if self.cf[i + 1] > self.cf[i] <= self.cf[i - 1]:
# if cfsmooth[i - 1] * (1 + aus1) >= cfsmooth[i]: # if cfsmooth[i - 1] * (1 + aus1) >= cfsmooth[i]:
@@ -508,9 +509,9 @@ class PragPicker(AutoPicker):
if flagpick_l > 0 and flagpick_r > 0 and cfpick_l <= 3 * cfpick_r: if flagpick_l > 0 and flagpick_r > 0 and cfpick_l <= 3 * cfpick_r:
self.Pick = pick_l self.Pick = pick_l
pickflag = 1 pickflag = 1
elif flagpick_l > 0 and flagpick_r > 0 and cfpick_l >= cfpick_r: # elif flagpick_l > 0 and flagpick_r > 0 and cfpick_l >= cfpick_r:
self.Pick = pick_r # self.Pick = pick_r
pickflag = 1 # pickflag = 1
elif flagpick_l == 0 and flagpick_r > 0 and cfpick_l >= cfpick_r: elif flagpick_l == 0 and flagpick_r > 0 and cfpick_l >= cfpick_r:
self.Pick = pick_l self.Pick = pick_l
pickflag = 1 pickflag = 1
+29 -18
View File
@@ -9,12 +9,13 @@
""" """
import warnings import warnings
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
import numpy as np import numpy as np
from scipy.signal import argrelmax
from obspy.core import Stream, UTCDateTime from obspy.core import Stream, UTCDateTime
from scipy.signal import argrelmax
from pylot.core.util.utils import get_Bool, get_None, SetChannelComponents from pylot.core.util.utils import get_bool, get_None, SetChannelComponents
def earllatepicker(X, nfac, TSNR, Pick1, iplot=0, verbosity=1, fig=None, linecolor='k'): def earllatepicker(X, nfac, TSNR, Pick1, iplot=0, verbosity=1, fig=None, linecolor='k'):
@@ -61,8 +62,8 @@ def earllatepicker(X, nfac, TSNR, Pick1, iplot=0, verbosity=1, fig=None, linecol
plt_flag = 0 plt_flag = 0
try: try:
iplot = int(iplot) iplot = int(iplot)
except: except ValueError:
if get_Bool(iplot): if get_bool(iplot):
iplot = 2 iplot = 2
else: else:
iplot = 0 iplot = 0
@@ -73,7 +74,7 @@ def earllatepicker(X, nfac, TSNR, Pick1, iplot=0, verbosity=1, fig=None, linecol
x = X[0].data x = X[0].data
t = np.linspace(0, X[0].stats.endtime - X[0].stats.starttime, t = np.linspace(0, X[0].stats.endtime - X[0].stats.starttime,
X[0].stats.npts) X[0].stats.npts)
inoise = getnoisewin(t, Pick1, TSNR[0], TSNR[1]) inoise = getnoisewin(t, Pick1, TSNR[0], TSNR[1])
# get signal window # get signal window
isignal = getsignalwin(t, Pick1, TSNR[2]) isignal = getsignalwin(t, Pick1, TSNR[2])
@@ -218,7 +219,7 @@ def fmpicker(Xraw, Xfilt, pickwin, Pick, iplot=0, fig=None, linecolor='k'):
xraw = Xraw[0].data xraw = Xraw[0].data
xfilt = Xfilt[0].data xfilt = Xfilt[0].data
t = np.linspace(0, Xraw[0].stats.endtime - Xraw[0].stats.starttime, t = np.linspace(0, Xraw[0].stats.endtime - Xraw[0].stats.starttime,
Xraw[0].stats.npts) Xraw[0].stats.npts)
# get pick window # get pick window
ipick = np.where((t <= min([Pick + pickwin, len(Xraw[0])])) & (t >= Pick)) ipick = np.where((t <= min([Pick + pickwin, len(Xraw[0])])) & (t >= Pick))
if len(ipick[0]) <= 1: if len(ipick[0]) <= 1:
@@ -274,7 +275,7 @@ def fmpicker(Xraw, Xfilt, pickwin, Pick, iplot=0, fig=None, linecolor='k'):
try: try:
P1 = np.polyfit(xslope1, xraw[islope1], 1) P1 = np.polyfit(xslope1, xraw[islope1], 1)
datafit1 = np.polyval(P1, xslope1) datafit1 = np.polyval(P1, xslope1)
except ValueError as e: except Exception as e:
print("fmpicker: Problems with data fit! {}".format(e)) print("fmpicker: Problems with data fit! {}".format(e))
print("Skip first motion determination!") print("Skip first motion determination!")
return FM return FM
@@ -320,7 +321,7 @@ def fmpicker(Xraw, Xfilt, pickwin, Pick, iplot=0, fig=None, linecolor='k'):
try: try:
P2 = np.polyfit(xslope2, xfilt[islope2], 1) P2 = np.polyfit(xslope2, xfilt[islope2], 1)
datafit2 = np.polyval(P2, xslope2) datafit2 = np.polyval(P2, xslope2)
except ValueError as e: except Exception as e:
emsg = 'fmpicker: polyfit failed: {}'.format(e) emsg = 'fmpicker: polyfit failed: {}'.format(e)
print(emsg) print(emsg)
return FM return FM
@@ -536,9 +537,10 @@ def getslopewin(Tcf, Pick, tslope):
:rtype: `numpy.ndarray` :rtype: `numpy.ndarray`
""" """
# TODO: fill out docstring # TODO: fill out docstring
slope = np.where( (Tcf <= min(Pick + tslope, Tcf[-1])) & (Tcf >= Pick) ) slope = np.where((Tcf <= min(Pick + tslope, Tcf[-1])) & (Tcf >= Pick))
return slope[0] return slope[0]
def getResolutionWindow(snr, extent): def getResolutionWindow(snr, extent):
""" """
Produce the half of the time resolution window width from given SNR value Produce the half of the time resolution window width from given SNR value
@@ -814,7 +816,7 @@ def checksignallength(X, pick, minsiglength, pickparams, iplot=0, fig=None, line
try: try:
iplot = int(iplot) iplot = int(iplot)
except: except:
if get_Bool(iplot): if get_bool(iplot):
iplot = 2 iplot = 2
else: else:
iplot = 0 iplot = 0
@@ -1128,7 +1130,7 @@ def checkZ4S(X, pick, pickparams, iplot, fig=None, linecolor='k'):
try: try:
iplot = int(iplot) iplot = int(iplot)
except: except:
if get_Bool(iplot): if get_bool(iplot):
iplot = 2 iplot = 2
else: else:
iplot = 0 iplot = 0
@@ -1207,7 +1209,7 @@ def checkZ4S(X, pick, pickparams, iplot, fig=None, linecolor='k'):
rms = rms_dict[key] rms = rms_dict[key]
trace = traces_dict[key] trace = traces_dict[key]
t = np.linspace(diff_dict[key], trace.stats.endtime - trace.stats.starttime + diff_dict[key], t = np.linspace(diff_dict[key], trace.stats.endtime - trace.stats.starttime + diff_dict[key],
trace.stats.npts) trace.stats.npts)
if i == 0: if i == 0:
if get_None(fig) is None: if get_None(fig) is None:
fig = plt.figure() # self.iplot) ### WHY? MP MP fig = plt.figure() # self.iplot) ### WHY? MP MP
@@ -1318,7 +1320,7 @@ def get_quality_class(uncertainty, weight_classes):
:return: quality of pick (0-4) :return: quality of pick (0-4)
:rtype: int :rtype: int
""" """
if not uncertainty: return max(weight_classes) if not uncertainty: return len(weight_classes)
try: try:
# create generator expression containing all indices of values in weight classes that are >= than uncertainty. # create generator expression containing all indices of values in weight classes that are >= than uncertainty.
# call next on it once to receive first value # call next on it once to receive first value
@@ -1329,6 +1331,7 @@ def get_quality_class(uncertainty, weight_classes):
quality = len(weight_classes) quality = len(weight_classes)
return quality return quality
def set_NaNs_to(data, nan_value): def set_NaNs_to(data, nan_value):
""" """
Replace all NaNs in data with nan_value Replace all NaNs in data with nan_value
@@ -1344,6 +1347,7 @@ def set_NaNs_to(data, nan_value):
data[nn] = nan_value data[nn] = nan_value
return data return data
def taper_cf(cf): def taper_cf(cf):
""" """
Taper cf data to get rid off of side maximas Taper cf data to get rid off of side maximas
@@ -1355,6 +1359,7 @@ def taper_cf(cf):
tap = np.hanning(len(cf)) tap = np.hanning(len(cf))
return tap * cf return tap * cf
def cf_positive(cf): def cf_positive(cf):
""" """
Shifts cf so that all values are positive Shifts cf so that all values are positive
@@ -1365,6 +1370,7 @@ def cf_positive(cf):
""" """
return cf + max(abs(cf)) return cf + max(abs(cf))
def smooth_cf(cf, t_smooth, delta): def smooth_cf(cf, t_smooth, delta):
""" """
Smooth cf by taking samples over t_smooth length Smooth cf by taking samples over t_smooth length
@@ -1393,6 +1399,7 @@ def smooth_cf(cf, t_smooth, delta):
cf_smooth -= offset # remove offset from smoothed function cf_smooth -= offset # remove offset from smoothed function
return cf_smooth return cf_smooth
def check_counts_ms(data): def check_counts_ms(data):
""" """
check if data is in counts or m/s check if data is in counts or m/s
@@ -1452,9 +1459,9 @@ def calcSlope(Data, datasmooth, Tcf, Pick, TSNR):
if imax == 0: if imax == 0:
print("AICPicker: Maximum for slope determination right at the beginning of the window!") print("AICPicker: Maximum for slope determination right at the beginning of the window!")
print("Choose longer slope determination window!") print("Choose longer slope determination window!")
raise IndexError raise IndexError
iislope = islope[0][0:imax + 1] # cut index so it contains only the first maximum iislope = islope[0][0:imax + 1] # cut index so it contains only the first maximum
dataslope = Data[0].data[iislope] # slope will only be calculated to the first maximum dataslope = Data[0].data[iislope] # slope will only be calculated to the first maximum
# calculate slope as polynomal fit of order 1 # calculate slope as polynomal fit of order 1
xslope = np.arange(0, len(dataslope)) xslope = np.arange(0, len(dataslope))
P = np.polyfit(xslope, dataslope, 1) P = np.polyfit(xslope, dataslope, 1)
@@ -1475,8 +1482,10 @@ def get_pickparams(pickparam):
:rtype: (dict, dict, dict, dict) :rtype: (dict, dict, dict, dict)
""" """
# Define names of all parameters in different groups # Define names of all parameters in different groups
p_parameter_names = 'algoP pstart pstop use_taup taup_model tlta tsnrz hosorder bpz1 bpz2 pickwinP aictsmooth tsmoothP ausP nfacP tpred1z tdet1z Parorder addnoise Precalcwin minAICPslope minAICPSNR timeerrorsP checkwindowP minfactorP'.split(' ') p_parameter_names = 'algoP pstart pstop use_taup taup_model tlta tsnrz hosorder bpz1 bpz2 pickwinP aictsmooth tsmoothP ausP nfacP tpred1z tdet1z Parorder addnoise Precalcwin minAICPslope minAICPSNR timeerrorsP checkwindowP minfactorP'.split(
s_parameter_names = 'algoS sstart sstop bph1 bph2 tsnrh pickwinS tpred1h tdet1h tpred2h tdet2h Sarorder aictsmoothS tsmoothS ausS minAICSslope minAICSSNR Srecalcwin nfacS timeerrorsS zfac checkwindowS minfactorS'.split(' ') ' ')
s_parameter_names = 'algoS sstart sstop bph1 bph2 tsnrh pickwinS tpred1h tdet1h tpred2h tdet2h Sarorder aictsmoothS tsmoothS ausS minAICSslope minAICSSNR Srecalcwin nfacS timeerrorsS zfac checkwindowS minfactorS'.split(
' ')
first_motion_names = 'minFMSNR fmpickwin minfmweight'.split(' ') first_motion_names = 'minFMSNR fmpickwin minfmweight'.split(' ')
signal_length_names = 'minsiglength minpercent noisefactor'.split(' ') signal_length_names = 'minsiglength minpercent noisefactor'.split(' ')
# Get list of values from pickparam by name # Get list of values from pickparam by name
@@ -1490,10 +1499,11 @@ def get_pickparams(pickparam):
first_motion_params = dict(zip(first_motion_names, fm_parameter_values)) first_motion_params = dict(zip(first_motion_names, fm_parameter_values))
signal_length_params = dict(zip(signal_length_names, sl_parameter_values)) signal_length_params = dict(zip(signal_length_names, sl_parameter_values))
p_params['use_taup'] = get_Bool(p_params['use_taup']) p_params['use_taup'] = get_bool(p_params['use_taup'])
return p_params, s_params, first_motion_params, signal_length_params return p_params, s_params, first_motion_params, signal_length_params
def getQualityFromUncertainty(uncertainty, Errors): def getQualityFromUncertainty(uncertainty, Errors):
# set initial quality to 4 (worst) and change only if one condition is hit # set initial quality to 4 (worst) and change only if one condition is hit
quality = 4 quality = 4
@@ -1517,6 +1527,7 @@ def getQualityFromUncertainty(uncertainty, Errors):
return quality return quality
if __name__ == '__main__': if __name__ == '__main__':
import doctest import doctest
+20 -22
View File
@@ -1,27 +1,22 @@
#!/usr/bin/env python #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import sys import traceback
import os
import matplotlib
from PySide2 import QtCore, QtGui, QtWidgets
from PySide2.QtCore import Qt
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
import matplotlib.patheffects as PathEffects
import cartopy.crs as ccrs import cartopy.crs as ccrs
import matplotlib.pyplot as plt
import cartopy.feature as cf import cartopy.feature as cf
from cartopy.mpl.gridliner import LongitudeFormatter, LatitudeFormatter import matplotlib
import matplotlib.patheffects as PathEffects
import traceback import matplotlib.pyplot as plt
import obspy
import numpy as np import numpy as np
import obspy
from PySide2 import QtWidgets
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from mpl_toolkits.axes_grid1.inset_locator import inset_axes from mpl_toolkits.axes_grid1.inset_locator import inset_axes
from scipy.interpolate import griddata from scipy.interpolate import griddata
from pylot.core.util.widgets import PickDlg
from pylot.core.pick.utils import get_quality_class from pylot.core.pick.utils import get_quality_class
from pylot.core.util.widgets import PickDlg
matplotlib.use('Qt5Agg') matplotlib.use('Qt5Agg')
@@ -42,7 +37,7 @@ class MplCanvas(FigureCanvas):
class Array_map(QtWidgets.QWidget): class Array_map(QtWidgets.QWidget):
def __init__(self, parent, metadata, parameter=None, axes=None, annotate=True, pointsize=25., def __init__(self, parent, metadata, parameter=None, axes=None, annotate=True, pointsize=25.,
linewidth=1.5, width=5e6, height=2e6): linewidth=1.5, width=5e6, height=2e6):
QtWidgets.QWidget.__init__(self) QtWidgets.QWidget.__init__(self, parent=parent)
assert (parameter is not None or parent is not None), 'either parent or parameter has to be set' assert (parameter is not None or parent is not None), 'either parent or parameter has to be set'
# set properties # set properties
@@ -80,7 +75,6 @@ class Array_map(QtWidgets.QWidget):
self._style = None if not hasattr(parent, '_style') else parent._style self._style = None if not hasattr(parent, '_style') else parent._style
self.show()
def init_map(self): def init_map(self):
self.init_colormap() self.init_colormap()
@@ -173,7 +167,8 @@ class Array_map(QtWidgets.QWidget):
self.canvas.fig.tight_layout() self.canvas.fig.tight_layout()
def add_merid_paral(self): def add_merid_paral(self):
self.gridlines = self.canvas.axes.gridlines(draw_labels=False, alpha=0.6, color='gray', linewidth=self.linewidth/2, zorder=7) self.gridlines = self.canvas.axes.gridlines(draw_labels=False, alpha=0.6, color='gray',
linewidth=self.linewidth / 2, zorder=7)
# TODO: current cartopy version does not support label removal. Devs are working on it. # TODO: current cartopy version does not support label removal. Devs are working on it.
# Should be fixed in coming cartopy versions # Should be fixed in coming cartopy versions
# self.gridlines.xformatter = LONGITUDE_FORMATTER # self.gridlines.xformatter = LONGITUDE_FORMATTER
@@ -468,20 +463,23 @@ class Array_map(QtWidgets.QWidget):
transform=ccrs.PlateCarree(), label='deleted')) transform=ccrs.PlateCarree(), label='deleted'))
def openPickDlg(self, ind): def openPickDlg(self, ind):
data = self._parent.get_data().getWFData() wfdata = self._parent.get_data().getWFData()
wfdata_comp = self._parent.get_data().getWFDataComp()
for index in ind: for index in ind:
network, station = self._station_onpick_ids[index].split('.')[:2] network, station = self._station_onpick_ids[index].split('.')[:2]
pyl_mw = self._parent pyl_mw = self._parent
try: try:
data = data.select(station=station) wfdata = wfdata.select(station=station)
if not data: wfdata_comp = wfdata_comp.select(station=station)
if not wfdata:
self._warn('No data for station {}'.format(station)) self._warn('No data for station {}'.format(station))
return return
pickDlg = PickDlg(self._parent, parameter=self.parameter, pickDlg = PickDlg(self._parent, parameter=self.parameter,
data=data, network=network, station=station, data=wfdata.copy(), data_compare=wfdata_comp.copy(), network=network, station=station,
picks=self._parent.get_current_event().getPick(station), picks=self._parent.get_current_event().getPick(station),
autopicks=self._parent.get_current_event().getAutopick(station), autopicks=self._parent.get_current_event().getAutopick(station),
filteroptions=self._parent.filteroptions, metadata=self.metadata, filteroptions=self._parent.filteroptions, metadata=self.metadata,
model=self.parameter.get('taup_model'),
event=pyl_mw.get_current_event()) event=pyl_mw.get_current_event())
except Exception as e: except Exception as e:
message = 'Could not generate Plot for station {st}.\n {er}'.format(st=station, er=e) message = 'Could not generate Plot for station {st}.\n {er}'.format(st=station, er=e)
@@ -513,7 +511,7 @@ class Array_map(QtWidgets.QWidget):
levels = np.linspace(self.get_min_from_picks(), self.get_max_from_picks(), nlevel) levels = np.linspace(self.get_min_from_picks(), self.get_max_from_picks(), nlevel)
self.contourf = self.canvas.axes.contourf(self.longrid, self.latgrid, self.picksgrid_active, levels, self.contourf = self.canvas.axes.contourf(self.longrid, self.latgrid, self.picksgrid_active, levels,
linewidths=self.linewidth*5, transform=ccrs.PlateCarree(), linewidths=self.linewidth * 5, transform=ccrs.PlateCarree(),
alpha=0.4, zorder=8, cmap=self.get_colormap()) alpha=0.4, zorder=8, cmap=self.get_colormap())
def get_colormap(self): def get_colormap(self):
+2 -1
View File
@@ -2,12 +2,13 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
try: try:
# noinspection PyUnresolvedReferences
from urllib2 import urlopen from urllib2 import urlopen
except: except:
from urllib.request import urlopen from urllib.request import urlopen
def checkurl(url='https://ariadne.geophysik.ruhr-uni-bochum.de/trac/PyLoT/'): def checkurl(url='https://git.geophysik.ruhr-uni-bochum.de/marcel/pylot/'):
""" """
check if URL is available check if URL is available
:param url: url :param url: url
+17 -14
View File
@@ -2,9 +2,10 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import glob import glob
import numpy as np
import os import os
import sys import sys
import numpy as np
from obspy import UTCDateTime, read_inventory, read from obspy import UTCDateTime, read_inventory, read
from obspy.io.xseed import Parser from obspy.io.xseed import Parser
@@ -46,7 +47,7 @@ class Metadata(object):
def __repr__(self): def __repr__(self):
return self.__str__() return self.__str__()
def add_inventory(self, path_to_inventory, obspy_dmt_inv = False): def add_inventory(self, path_to_inventory, obspy_dmt_inv=False):
""" """
Add path to list of inventories. Add path to list of inventories.
:param path_to_inventory: Path to a folder :param path_to_inventory: Path to a folder
@@ -211,6 +212,7 @@ class Metadata(object):
self.stations_dict[st_id] = {'latitude': station[0].latitude, self.stations_dict[st_id] = {'latitude': station[0].latitude,
'longitude': station[0].longitude, 'longitude': station[0].longitude,
'elevation': station[0].elevation} 'elevation': station[0].elevation}
read_stat = {'xml': stat_info_from_inventory, read_stat = {'xml': stat_info_from_inventory,
'dless': stat_info_from_parser} 'dless': stat_info_from_parser}
@@ -269,7 +271,7 @@ class Metadata(object):
continue continue
invtype, robj = self._read_metadata_file(os.path.join(path_to_inventory, fname)) invtype, robj = self._read_metadata_file(os.path.join(path_to_inventory, fname))
try: try:
robj.get_coordinates(station_seed_id) # robj.get_coordinates(station_seed_id) # TODO: Commented out, failed with Parser, is this needed?
self.inventory_files[fname] = {'invtype': invtype, self.inventory_files[fname] = {'invtype': invtype,
'data': robj} 'data': robj}
if station_seed_id in self.seed_ids.keys(): if station_seed_id in self.seed_ids.keys():
@@ -351,25 +353,25 @@ def check_time(datetime):
:type datetime: list :type datetime: list
:return: returns True if Values are in supposed range, returns False otherwise :return: returns True if Values are in supposed range, returns False otherwise
>>> check_time([1999, 01, 01, 23, 59, 59, 999000]) >>> check_time([1999, 1, 1, 23, 59, 59, 999000])
True True
>>> check_time([1999, 01, 01, 23, 59, 60, 999000]) >>> check_time([1999, 1, 1, 23, 59, 60, 999000])
False False
>>> check_time([1999, 01, 01, 23, 59, 59, 1000000]) >>> check_time([1999, 1, 1, 23, 59, 59, 1000000])
False False
>>> check_time([1999, 01, 01, 23, 60, 59, 999000]) >>> check_time([1999, 1, 1, 23, 60, 59, 999000])
False False
>>> check_time([1999, 01, 01, 23, 60, 59, 999000]) >>> check_time([1999, 1, 1, 23, 60, 59, 999000])
False False
>>> check_time([1999, 01, 01, 24, 59, 59, 999000]) >>> check_time([1999, 1, 1, 24, 59, 59, 999000])
False False
>>> check_time([1999, 01, 31, 23, 59, 59, 999000]) >>> check_time([1999, 1, 31, 23, 59, 59, 999000])
True True
>>> check_time([1999, 02, 30, 23, 59, 59, 999000]) >>> check_time([1999, 2, 30, 23, 59, 59, 999000])
False False
>>> check_time([1999, 02, 29, 23, 59, 59, 999000]) >>> check_time([1999, 2, 29, 23, 59, 59, 999000])
False False
>>> check_time([2000, 02, 29, 23, 59, 59, 999000]) >>> check_time([2000, 2, 29, 23, 59, 59, 999000])
True True
>>> check_time([2000, 13, 29, 23, 59, 59, 999000]) >>> check_time([2000, 13, 29, 23, 59, 59, 999000])
False False
@@ -380,6 +382,7 @@ def check_time(datetime):
except ValueError: except ValueError:
return False return False
# TODO: change root to datapath # TODO: change root to datapath
def get_file_list(root_dir): def get_file_list(root_dir):
""" """
@@ -444,7 +447,7 @@ def evt_head_check(root_dir, out_dir=None):
""" """
if not out_dir: if not out_dir:
print('WARNING files are going to be overwritten!') print('WARNING files are going to be overwritten!')
inp = str(raw_input('Continue? [y/N]')) inp = str(input('Continue? [y/N]'))
if not inp == 'y': if not inp == 'y':
sys.exit() sys.exit()
filelist = get_file_list(root_dir) filelist = get_file_list(root_dir)
+1 -3
View File
@@ -26,9 +26,7 @@ elif system_name == "Windows":
# suffix for phase name if not phase identified by last letter (P, p, etc.) # suffix for phase name if not phase identified by last letter (P, p, etc.)
ALTSUFFIX = ['diff', 'n', 'g', '1', '2', '3'] ALTSUFFIX = ['diff', 'n', 'g', '1', '2', '3']
FILTERDEFAULTS = readDefaultFilterInformation(os.path.join(os.path.expanduser('~'), FILTERDEFAULTS = readDefaultFilterInformation()
'.pylot',
'pylot.in'))
TIMEERROR_DEFAULTS = os.path.join(os.path.expanduser('~'), TIMEERROR_DEFAULTS = os.path.join(os.path.expanduser('~'),
'.pylot', '.pylot',
+1
View File
@@ -2,6 +2,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import os import os
from obspy import UTCDateTime from obspy import UTCDateTime
from obspy.core.event import Event as ObsPyEvent from obspy.core.event import Event as ObsPyEvent
from obspy.core.event import Origin, ResourceIdentifier from obspy.core.event import Origin, ResourceIdentifier
+4 -2
View File
@@ -3,6 +3,7 @@
# small script that creates array maps for each event within a previously generated PyLoT project # small script that creates array maps for each event within a previously generated PyLoT project
import os import os
num_thread = "16" num_thread = "16"
os.environ["OMP_NUM_THREADS"] = num_thread os.environ["OMP_NUM_THREADS"] = num_thread
os.environ["OPENBLAS_NUM_THREADS"] = num_thread os.environ["OPENBLAS_NUM_THREADS"] = num_thread
@@ -15,6 +16,7 @@ import multiprocessing
import sys import sys
import glob import glob
import matplotlib import matplotlib
matplotlib.use('Qt5Agg') matplotlib.use('Qt5Agg')
sys.path.append(os.path.join('/'.join(sys.argv[0].split('/')[:-1]), '../../..')) sys.path.append(os.path.join('/'.join(sys.argv[0].split('/')[:-1]), '../../..'))
@@ -52,7 +54,8 @@ def array_map_worker(input_dict):
print('Working on event: {} ({}/{})'.format(eventdir, input_dict['index'] + 1, input_dict['nEvents'])) print('Working on event: {} ({}/{})'.format(eventdir, input_dict['index'] + 1, input_dict['nEvents']))
xml_picks = glob.glob(os.path.join(eventdir, f'*{input_dict["f_ext"]}.xml')) xml_picks = glob.glob(os.path.join(eventdir, f'*{input_dict["f_ext"]}.xml'))
if not len(xml_picks): if not len(xml_picks):
print('Event {} does not have any picks associated with event file extension {}'. format(eventdir, input_dict['f_ext'])) print('Event {} does not have any picks associated with event file extension {}'.format(eventdir,
input_dict['f_ext']))
return return
# check for picks # check for picks
manualpicks = event.getPicks() manualpicks = event.getPicks()
@@ -92,4 +95,3 @@ if __name__ == '__main__':
for infile in args.infiles: for infile in args.infiles:
main(os.path.join(args.dataroot, infile), f_ext='_correlated_0.03-0.1', ncores=args.ncores) main(os.path.join(args.dataroot, infile), f_ext='_correlated_0.03-0.1', ncores=args.ncores)
+1 -3
View File
@@ -11,7 +11,6 @@ try:
except Exception as e: except Exception as e:
print('Warning: Could not import module QtCore.') print('Warning: Could not import module QtCore.')
from pylot.core.util.utils import pick_color from pylot.core.util.utils import pick_color
@@ -57,7 +56,7 @@ def which(program, parameter):
nllocpath = ":" + parameter.get('nllocbin') nllocpath = ":" + parameter.get('nllocbin')
os.environ['PATH'] += nllocpath os.environ['PATH'] += nllocpath
except Exception as e: except Exception as e:
print(e.message) print(e)
def is_exe(fpath): def is_exe(fpath):
return os.path.exists(fpath) and os.access(fpath, os.X_OK) return os.path.exists(fpath) and os.access(fpath, os.X_OK)
@@ -101,4 +100,3 @@ def make_pen(picktype, phase, key, quality):
linestyle, width = pick_linestyle_pg(picktype, key) linestyle, width = pick_linestyle_pg(picktype, key)
pen = pg.mkPen(rgba, width=width, style=linestyle) pen = pg.mkPen(rgba, width=width, style=linestyle)
return pen return pen
+3 -2
View File
@@ -2,6 +2,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import os import os
from obspy import UTCDateTime from obspy import UTCDateTime
@@ -36,12 +37,12 @@ def qml_from_obspyDMT(path):
return IOError('Could not find Event at {}'.format(path)) return IOError('Could not find Event at {}'.format(path))
with open(path, 'rb') as infile: with open(path, 'rb') as infile:
event_dmt = pickle.load(infile)#, fix_imports=True) event_dmt = pickle.load(infile) # , fix_imports=True)
event_dmt['origin_id'].id = str(event_dmt['origin_id'].id) event_dmt['origin_id'].id = str(event_dmt['origin_id'].id)
ev = Event(resource_id=event_dmt['event_id']) ev = Event(resource_id=event_dmt['event_id'])
#small bugfix "unhashable type: 'newstr' " # small bugfix "unhashable type: 'newstr' "
event_dmt['origin_id'].id = str(event_dmt['origin_id'].id) event_dmt['origin_id'].id = str(event_dmt['origin_id'].id)
origin = Origin(resource_id=event_dmt['origin_id'], origin = Origin(resource_id=event_dmt['origin_id'],
+2 -1
View File
@@ -1,8 +1,9 @@
#!/usr/bin/env python #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import numpy as np
import warnings import warnings
import numpy as np
from obspy import UTCDateTime from obspy import UTCDateTime
from pylot.core.util.utils import fit_curve, clims from pylot.core.util.utils import fit_curve, clims
+16 -6
View File
@@ -1,6 +1,9 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import sys, os, traceback
import multiprocessing import multiprocessing
import os
import sys
import traceback
from PySide2.QtCore import QThread, Signal, Qt, Slot, QRunnable, QObject from PySide2.QtCore import QThread, Signal, Qt, Slot, QRunnable, QObject
from PySide2.QtWidgets import QDialog, QProgressBar, QLabel, QHBoxLayout, QPushButton from PySide2.QtWidgets import QDialog, QProgressBar, QLabel, QHBoxLayout, QPushButton
@@ -19,9 +22,11 @@ class Thread(QThread):
self.abortButton = abortButton self.abortButton = abortButton
self.finished.connect(self.hideProgressbar) self.finished.connect(self.hideProgressbar)
self.showProgressbar() self.showProgressbar()
self.old_stdout = None
def run(self): def run(self):
if self.redirect_stdout: if self.redirect_stdout:
self.old_stdout = sys.stdout
sys.stdout = self sys.stdout = self
try: try:
if self.arg is not None: if self.arg is not None:
@@ -36,7 +41,8 @@ class Thread(QThread):
exctype, value = sys.exc_info()[:2] exctype, value = sys.exc_info()[:2]
self._executedErrorInfo = '{} {} {}'. \ self._executedErrorInfo = '{} {} {}'. \
format(exctype, value, traceback.format_exc()) format(exctype, value, traceback.format_exc())
sys.stdout = sys.__stdout__ if self.redirect_stdout:
sys.stdout = self.old_stdout
def showProgressbar(self): def showProgressbar(self):
if self.progressText: if self.progressText:
@@ -93,23 +99,25 @@ class Worker(QRunnable):
self.progressText = progressText self.progressText = progressText
self.pb_widget = pb_widget self.pb_widget = pb_widget
self.redirect_stdout = redirect_stdout self.redirect_stdout = redirect_stdout
self.old_stdout = None
@Slot() @Slot()
def run(self): def run(self):
if self.redirect_stdout: if self.redirect_stdout:
self.old_stdout = sys.stdout
sys.stdout = self sys.stdout = self
try: try:
result = self.fun(self.args) result = self.fun(self.args)
except: except:
exctype, value = sys.exc_info ()[:2] exctype, value = sys.exc_info()[:2]
print(exctype, value, traceback.format_exc()) print(exctype, value, traceback.format_exc())
self.signals.error.emit ((exctype, value, traceback.format_exc ())) self.signals.error.emit((exctype, value, traceback.format_exc()))
else: else:
self.signals.result.emit(result) self.signals.result.emit(result)
finally: finally:
self.signals.finished.emit('Done') self.signals.finished.emit('Done')
sys.stdout = sys.__stdout__ sys.stdout = self.old_stdout
def write(self, text): def write(self, text):
self.signals.message.emit(text) self.signals.message.emit(text)
@@ -141,11 +149,13 @@ class MultiThread(QThread):
self.progressText = progressText self.progressText = progressText
self.pb_widget = pb_widget self.pb_widget = pb_widget
self.redirect_stdout = redirect_stdout self.redirect_stdout = redirect_stdout
self.old_stdout = None
self.finished.connect(self.hideProgressbar) self.finished.connect(self.hideProgressbar)
self.showProgressbar() self.showProgressbar()
def run(self): def run(self):
if self.redirect_stdout: if self.redirect_stdout:
self.old_stdout = sys.stdout
sys.stdout = self sys.stdout = self
try: try:
if not self.ncores: if not self.ncores:
@@ -161,7 +171,7 @@ class MultiThread(QThread):
exc_type, exc_obj, exc_tb = sys.exc_info() exc_type, exc_obj, exc_tb = sys.exc_info()
fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
print('Exception: {}, file: {}, line: {}'.format(exc_type, fname, exc_tb.tb_lineno)) print('Exception: {}, file: {}, line: {}'.format(exc_type, fname, exc_tb.tb_lineno))
sys.stdout = sys.__stdout__ sys.stdout = self.old_stdout
def showProgressbar(self): def showProgressbar(self):
if self.progressText: if self.progressText:
+11 -10
View File
@@ -2,12 +2,13 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import hashlib import hashlib
import numpy as np
import os import os
import platform import platform
import re import re
import subprocess import subprocess
import warnings import warnings
import numpy as np
from obspy import UTCDateTime, read from obspy import UTCDateTime, read
from obspy.core import AttribDict from obspy.core import AttribDict
from obspy.signal.rotate import rotate2zne from obspy.signal.rotate import rotate2zne
@@ -36,15 +37,14 @@ def getAutoFilteroptions(phase, parameter):
return filteroptions return filteroptions
def readDefaultFilterInformation(fname): def readDefaultFilterInformation():
""" """
Read default filter information from pylot.in file Read default filter information from pylot.in file
:param fname: path to pylot.in file
:type fname: str
:return: dictionary containing the defailt filter information :return: dictionary containing the defailt filter information
:rtype: dict :rtype: dict
""" """
pparam = PylotParameter(fname) pparam = PylotParameter()
pparam.reset_defaults()
return readFilterInformation(pparam) return readFilterInformation(pparam)
@@ -327,7 +327,7 @@ def get_None(value):
return value return value
def get_Bool(value): def get_bool(value):
""" """
Convert string representations of bools to their true boolean value Convert string representations of bools to their true boolean value
:param value: :param value:
@@ -335,13 +335,14 @@ def get_Bool(value):
:return: true boolean value :return: true boolean value
:rtype: bool :rtype: bool
""" """
if value in ['True', 'true']: if type(value) is bool:
return value
elif value in ['True', 'true']:
return True return True
elif value in ['False', 'false']: elif value in ['False', 'false']:
return False return False
else: else:
return value return bool(value)
def four_digits(year): def four_digits(year):
""" """
@@ -1168,7 +1169,7 @@ def correct_iplot(iplot):
try: try:
iplot = int(iplot) iplot = int(iplot)
except ValueError: except ValueError:
if get_Bool(iplot): if get_bool(iplot):
iplot = 2 iplot = 2
else: else:
iplot = 0 iplot = 0
+1 -1
View File
@@ -35,9 +35,9 @@ from __future__ import print_function
__all__ = "get_git_version" __all__ = "get_git_version"
import inspect
# NO IMPORTS FROM PYLOT IN THIS FILE! (file gets used at installation time) # NO IMPORTS FROM PYLOT IN THIS FILE! (file gets used at installation time)
import os import os
import inspect
from subprocess import Popen, PIPE from subprocess import Popen, PIPE
# NO IMPORTS FROM PYLOT IN THIS FILE! (file gets used at installation time) # NO IMPORTS FROM PYLOT IN THIS FILE! (file gets used at installation time)
+452 -144
View File
File diff suppressed because it is too large Load Diff
+12
View File
@@ -0,0 +1,12 @@
# This file may be used to create an environment using:
# $ conda create --name <env> --file <this file>
# platform: win-64
cartopy=0.20.2
matplotlib-base=3.3.4
numpy=1.22.3
obspy=1.3.0
pyqtgraph=0.12.4
pyside2=5.13.2
python=3.8.12
qt=5.12.9
scipy=1.8.0
-17
View File
@@ -1,17 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from distutils.core import setup
setup(
name='PyLoT',
version='0.2',
packages=['pylot', 'pylot.core', 'pylot.core.loc', 'pylot.core.pick',
'pylot.core.io', 'pylot.core.util', 'pylot.core.active',
'pylot.core.analysis', 'pylot.testing'],
requires=['obspy', 'PySide2', 'matplotlib', 'numpy', 'scipy', 'pyqtgraph', 'cartopy'],
url='dummy',
license='LGPLv3',
author='Sebastian Wehling-Benatelli',
author_email='sebastian.wehling@rub.de',
description='Comprehensive Python picking and Location Toolbox for seismological data.'
)
File diff suppressed because it is too large Load Diff
+4 -2
View File
@@ -1,6 +1,8 @@
import unittest import unittest
from pylot.core.pick.autopick import PickingResults from pylot.core.pick.autopick import PickingResults
class TestPickingResults(unittest.TestCase): class TestPickingResults(unittest.TestCase):
def setUp(self): def setUp(self):
@@ -70,9 +72,9 @@ class TestPickingResults(unittest.TestCase):
curr_len = len(self.pr) curr_len = len(self.pr)
except Exception: except Exception:
self.fail("test_dunder_attributes overwrote an instance internal dunder method") self.fail("test_dunder_attributes overwrote an instance internal dunder method")
self.assertEqual(prev_len+1, curr_len) # +1 for the added __len__ key/value-pair self.assertEqual(prev_len + 1, curr_len) # +1 for the added __len__ key/value-pair
self.pr.__len__ = 42 self.pr.__len__ = 42
self.assertEqual(42, self.pr['__len__']) self.assertEqual(42, self.pr['__len__'])
self.assertEqual(prev_len+1, curr_len, msg="__len__ was overwritten") self.assertEqual(prev_len + 1, curr_len, msg="__len__ was overwritten")
+9 -6
View File
@@ -1,5 +1,6 @@
import os import os
import unittest import unittest
from obspy import UTCDateTime from obspy import UTCDateTime
from obspy.io.xseed import Parser from obspy.io.xseed import Parser
from obspy.io.xseed.utils import SEEDParserException from obspy.io.xseed.utils import SEEDParserException
@@ -27,7 +28,7 @@ class TestMetadata(unittest.TestCase):
result = {} result = {}
for channel in ('Z', 'N', 'E'): for channel in ('Z', 'N', 'E'):
with HidePrints(): with HidePrints():
coords = self.m.get_coordinates(self.station_id+channel, time=self.time) coords = self.m.get_coordinates(self.station_id + channel, time=self.time)
result[channel] = coords result[channel] = coords
self.assertDictEqual(result[channel], expected[channel]) self.assertDictEqual(result[channel], expected[channel])
@@ -42,7 +43,7 @@ class TestMetadata(unittest.TestCase):
result = {} result = {}
for channel in ('Z', 'N', 'E'): for channel in ('Z', 'N', 'E'):
with HidePrints(): with HidePrints():
coords = self.m.get_coordinates(self.station_id+channel) coords = self.m.get_coordinates(self.station_id + channel)
result[channel] = coords result[channel] = coords
self.assertDictEqual(result[channel], expected[channel]) self.assertDictEqual(result[channel], expected[channel])
@@ -145,7 +146,7 @@ class TestMetadata_read_single_file(unittest.TestCase):
def test_read_single_file(self): def test_read_single_file(self):
"""Test if reading a single file works""" """Test if reading a single file works"""
fname = os.path.join(self.metadata_folders[0], 'DATALESS.'+self.station_id) fname = os.path.join(self.metadata_folders[0], 'DATALESS.' + self.station_id)
with HidePrints(): with HidePrints():
res = self.m.read_single_file(fname) res = self.m.read_single_file(fname)
# method should return true if file is successfully read # method should return true if file is successfully read
@@ -172,7 +173,7 @@ class TestMetadata_read_single_file(unittest.TestCase):
def test_read_single_file_multiple_times(self): def test_read_single_file_multiple_times(self):
"""Test if reading a file twice doesnt add it twice to the metadata object""" """Test if reading a file twice doesnt add it twice to the metadata object"""
fname = os.path.join(self.metadata_folders[0], 'DATALESS.'+self.station_id) fname = os.path.join(self.metadata_folders[0], 'DATALESS.' + self.station_id)
with HidePrints(): with HidePrints():
res1 = self.m.read_single_file(fname) res1 = self.m.read_single_file(fname)
res2 = self.m.read_single_file(fname) res2 = self.m.read_single_file(fname)
@@ -197,7 +198,8 @@ class TestMetadataMultipleTime(unittest.TestCase):
def setUp(self): def setUp(self):
self.seed_id = 'LE.ROTT..HN' self.seed_id = 'LE.ROTT..HN'
path = os.path.dirname(__file__) # gets path to currently running script path = os.path.dirname(__file__) # gets path to currently running script
metadata = os.path.join('test_data', 'dless_multiple_times', 'MAGS2_LE_ROTT.dless') # specific subfolder of test data metadata = os.path.join('test_data', 'dless_multiple_times',
'MAGS2_LE_ROTT.dless') # specific subfolder of test data
metadata_path = os.path.join(path, metadata) metadata_path = os.path.join(path, metadata)
self.m = Metadata(metadata_path) self.m = Metadata(metadata_path)
self.p = Parser(metadata_path) self.p = Parser(metadata_path)
@@ -299,7 +301,8 @@ Channels:
def setUp(self): def setUp(self):
self.seed_id = 'KB.TMO07.00.HHZ' self.seed_id = 'KB.TMO07.00.HHZ'
path = os.path.dirname(__file__) # gets path to currently running script path = os.path.dirname(__file__) # gets path to currently running script
metadata = os.path.join('test_data', 'dless_multiple_instruments', 'MAGS2_KB_TMO07.dless') # specific subfolder of test data metadata = os.path.join('test_data', 'dless_multiple_instruments',
'MAGS2_KB_TMO07.dless') # specific subfolder of test data
metadata_path = os.path.join(path, metadata) metadata_path = os.path.join(path, metadata)
self.m = Metadata(metadata_path) self.m = Metadata(metadata_path)
self.p = Parser(metadata_path) self.p = Parser(metadata_path)
-35
View File
@@ -1,35 +0,0 @@
import unittest
from pylot.core.pick.autopick import PickingParameters
class TestPickingParameters(unittest.TestCase):
def setUp(self):
self.simple_dict = {'a': 3, 'b': 14}
self.nested_dict = {'a': self.simple_dict, 'b': self.simple_dict}
def assertParameterEquality(self, dic, instance):
"""Test wether all parameters given in dic are found in instance"""
for key, value in dic.items():
self.assertEqual(value, getattr(instance, key))
def test_add_params_from_dict_simple(self):
pickparam = PickingParameters()
pickparam.add_params_from_dict(self.simple_dict)
self.assertParameterEquality(self.simple_dict, pickparam)
def test_add_params_from_dict_nested(self):
pickparam = PickingParameters()
pickparam.add_params_from_dict(self.nested_dict)
self.assertParameterEquality(self.nested_dict, pickparam)
def test_init(self):
pickparam = PickingParameters(self.simple_dict)
self.assertParameterEquality(self.simple_dict, pickparam)
def test_dot_access(self):
pickparam = PickingParameters(self.simple_dict)
self.assertEqual(pickparam.a, self.simple_dict['a'])
if __name__ == '__main__':
unittest.main()
@@ -1,21 +1,21 @@
<?xml version='1.0' encoding='utf-8'?> <?xml version='1.0' encoding='utf-8'?>
<q:quakeml xmlns:q="http://quakeml.org/xmlns/quakeml/1.2" xmlns="http://quakeml.org/xmlns/bed/1.2"> <q:quakeml xmlns:q="http://quakeml.org/xmlns/quakeml/1.2" xmlns="http://quakeml.org/xmlns/bed/1.2">
<eventParameters publicID="smi:local/53a38563-739a-48b2-9f34-bf40ee7b656a"> <eventParameters publicID="smi:local/53a38563-739a-48b2-9f34-bf40ee7b656a">
<event publicID="smi:local/e0001.024.16"> <event publicID="smi:local/e0001.024.16">
<origin publicID="smi:local/e0001.024.16"> <origin publicID="smi:local/e0001.024.16">
<time> <time>
<value>2016-01-24T10:30:30.000000Z</value> <value>2016-01-24T10:30:30.000000Z</value>
</time> </time>
<latitude> <latitude>
<value>59.66</value> <value>59.66</value>
</latitude> </latitude>
<longitude> <longitude>
<value>-153.45</value> <value>-153.45</value>
</longitude> </longitude>
<depth> <depth>
<value>128.0</value> <value>128.0</value>
</depth> </depth>
</origin> </origin>
</event> </event>
</eventParameters> </eventParameters>
</q:quakeml> </q:quakeml>
@@ -1,12 +1,13 @@
import unittest
from unittest import skip
import obspy
from obspy import UTCDateTime
import os import os
import sys import sys
from pylot.core.pick.autopick import autopickstation import unittest
from pylot.core.io.inputs import PylotParameter
import obspy
from obspy import UTCDateTime
from pylot.core.io.data import Data from pylot.core.io.data import Data
from pylot.core.io.inputs import PylotParameter
from pylot.core.pick.autopick import autopickstation
from pylot.core.util.utils import trim_station_components from pylot.core.util.utils import trim_station_components
@@ -93,51 +94,100 @@ class TestAutopickStation(unittest.TestCase):
self.inputfile_taupy_disabled = os.path.join(os.path.dirname(__file__), 'autoPyLoT_global_taupy_false.in') self.inputfile_taupy_disabled = os.path.join(os.path.dirname(__file__), 'autoPyLoT_global_taupy_false.in')
self.pickparam_taupy_enabled = PylotParameter(fnin=self.inputfile_taupy_enabled) self.pickparam_taupy_enabled = PylotParameter(fnin=self.inputfile_taupy_enabled)
self.pickparam_taupy_disabled = PylotParameter(fnin=self.inputfile_taupy_disabled) self.pickparam_taupy_disabled = PylotParameter(fnin=self.inputfile_taupy_disabled)
self.xml_file = os.path.join(os.path.dirname(__file__),self.event_id, 'PyLoT_'+self.event_id+'.xml') self.xml_file = os.path.join(os.path.dirname(__file__), self.event_id, 'PyLoT_' + self.event_id + '.xml')
self.data = Data(evtdata=self.xml_file) self.data = Data(evtdata=self.xml_file)
# create origin for taupy testing # create origin for taupy testing
self.origin = [obspy.core.event.origin.Origin(magnitude=7.1, latitude=59.66, longitude=-153.45, depth=128.0, time=UTCDateTime("2016-01-24T10:30:30.0"))] self.origin = [obspy.core.event.origin.Origin(magnitude=7.1, latitude=59.66, longitude=-153.45, depth=128.0,
time=UTCDateTime("2016-01-24T10:30:30.0"))]
# mocking metadata since reading it takes a long time to read from file # mocking metadata since reading it takes a long time to read from file
self.metadata = MockMetadata() self.metadata = MockMetadata()
# show complete diff when difference in results dictionaries are found # show complete diff when difference in results dictionaries are found
self.maxDiff = None self.maxDiff = None
#@skip("Works") # @skip("Works")
def test_autopickstation_taupy_disabled_gra1(self): def test_autopickstation_taupy_disabled_gra1(self):
expected = {'P': {'picker': 'auto', 'snrdb': 15.405649120980094, 'weight': 0, 'Mo': None, 'marked': [], 'Mw': None, 'fc': None, 'snr': 34.718816470730317, 'mpp': UTCDateTime(2016, 1, 24, 10, 41, 31, 690000), 'w0': None, 'spe': 0.93333333333333235, 'network': u'GR', 'epp': UTCDateTime(2016, 1, 24, 10, 41, 28, 890000), 'lpp': UTCDateTime(2016, 1, 24, 10, 41, 32, 690000), 'fm': 'D', 'channel': u'LHZ'}, 'S': {'picker': 'auto', 'snrdb': 10.669661906545489, 'network': u'GR', 'weight': 0, 'Ao': None, 'lpp': UTCDateTime(2016, 1, 24, 10, 50, 30, 690000), 'snr': 11.667187857573905, 'epp': UTCDateTime(2016, 1, 24, 10, 50, 21, 690000), 'mpp': UTCDateTime(2016, 1, 24, 10, 50, 29, 690000), 'fm': None, 'spe': 2.6666666666666665, 'channel': u'LHE'}} expected = {
'P': {'picker': 'auto', 'snrdb': 15.405649120980094, 'weight': 0, 'Mo': None, 'marked': [], 'Mw': None,
'fc': None, 'snr': 34.718816470730317, 'mpp': UTCDateTime(2016, 1, 24, 10, 41, 31, 690000),
'w0': None, 'spe': 0.93333333333333235, 'network': u'GR',
'epp': UTCDateTime(2016, 1, 24, 10, 41, 28, 890000),
'lpp': UTCDateTime(2016, 1, 24, 10, 41, 32, 690000), 'fm': 'D', 'channel': u'LHZ'},
'S': {'picker': 'auto', 'snrdb': 10.669661906545489, 'network': u'GR', 'weight': 0, 'Ao': None,
'lpp': UTCDateTime(2016, 1, 24, 10, 50, 30, 690000), 'snr': 11.667187857573905,
'epp': UTCDateTime(2016, 1, 24, 10, 50, 21, 690000),
'mpp': UTCDateTime(2016, 1, 24, 10, 50, 29, 690000), 'fm': None, 'spe': 2.6666666666666665,
'channel': u'LHE'}}
with HidePrints(): with HidePrints():
result, station = autopickstation(wfstream=self.gra1, pickparam=self.pickparam_taupy_disabled, metadata=(None, None)) result, station = autopickstation(wfstream=self.gra1, pickparam=self.pickparam_taupy_disabled,
metadata=(None, None))
self.assertDictContainsSubset(expected=expected['P'], actual=result['P']) self.assertDictContainsSubset(expected=expected['P'], actual=result['P'])
self.assertDictContainsSubset(expected=expected['S'], actual=result['S']) self.assertDictContainsSubset(expected=expected['S'], actual=result['S'])
self.assertEqual('GRA1', station) self.assertEqual('GRA1', station)
def test_autopickstation_taupy_enabled_gra1(self): def test_autopickstation_taupy_enabled_gra1(self):
expected = {'P': {'picker': 'auto', 'snrdb': 15.599905299126778, 'weight': 0, 'Mo': None, 'marked': [], 'Mw': None, 'fc': None, 'snr': 36.307013769185403, 'mpp': UTCDateTime(2016, 1, 24, 10, 41, 27, 690000), 'w0': None, 'spe': 0.93333333333333235, 'network': u'GR', 'epp': UTCDateTime(2016, 1, 24, 10, 41, 24, 890000), 'lpp': UTCDateTime(2016, 1, 24, 10, 41, 28, 690000), 'fm': 'U', 'channel': u'LHZ'}, 'S': {'picker': 'auto', 'snrdb': 10.669661906545489, 'network': u'GR', 'weight': 0, 'Ao': None, 'lpp': UTCDateTime(2016, 1, 24, 10, 50, 30, 690000), 'snr': 11.667187857573905, 'epp': UTCDateTime(2016, 1, 24, 10, 50, 21, 690000), 'mpp': UTCDateTime(2016, 1, 24, 10, 50, 29, 690000), 'fm': None, 'spe': 2.6666666666666665, 'channel': u'LHE'}} expected = {
'P': {'picker': 'auto', 'snrdb': 15.599905299126778, 'weight': 0, 'Mo': None, 'marked': [], 'Mw': None,
'fc': None, 'snr': 36.307013769185403, 'mpp': UTCDateTime(2016, 1, 24, 10, 41, 27, 690000),
'w0': None, 'spe': 0.93333333333333235, 'network': u'GR',
'epp': UTCDateTime(2016, 1, 24, 10, 41, 24, 890000),
'lpp': UTCDateTime(2016, 1, 24, 10, 41, 28, 690000), 'fm': 'U', 'channel': u'LHZ'},
'S': {'picker': 'auto', 'snrdb': 10.669661906545489, 'network': u'GR', 'weight': 0, 'Ao': None,
'lpp': UTCDateTime(2016, 1, 24, 10, 50, 30, 690000), 'snr': 11.667187857573905,
'epp': UTCDateTime(2016, 1, 24, 10, 50, 21, 690000),
'mpp': UTCDateTime(2016, 1, 24, 10, 50, 29, 690000), 'fm': None, 'spe': 2.6666666666666665,
'channel': u'LHE'}}
with HidePrints(): with HidePrints():
result, station = autopickstation(wfstream=self.gra1, pickparam=self.pickparam_taupy_enabled, metadata=self.metadata, origin=self.origin) result, station = autopickstation(wfstream=self.gra1, pickparam=self.pickparam_taupy_enabled,
metadata=self.metadata, origin=self.origin)
self.assertDictContainsSubset(expected=expected['P'], actual=result['P']) self.assertDictContainsSubset(expected=expected['P'], actual=result['P'])
self.assertDictContainsSubset(expected=expected['S'], actual=result['S']) self.assertDictContainsSubset(expected=expected['S'], actual=result['S'])
self.assertEqual('GRA1', station) self.assertEqual('GRA1', station)
def test_autopickstation_taupy_disabled_gra2(self): def test_autopickstation_taupy_disabled_gra2(self):
expected = {'P': {'picker': 'auto', 'snrdb': None, 'weight': 9, 'Mo': None, 'marked': 'shortsignallength', 'Mw': None, 'fc': None, 'snr': None, 'mpp': UTCDateTime(2016, 1, 24, 10, 36, 59, 150000), 'w0': None, 'spe': None, 'network': u'GR', 'epp': UTCDateTime(2016, 1, 24, 10, 36, 43, 150000), 'lpp': UTCDateTime(2016, 1, 24, 10, 37, 15, 150000), 'fm': 'N', 'channel': u'LHZ'}, 'S': {'picker': 'auto', 'snrdb': None, 'network': u'GR', 'weight': 4, 'Ao': None, 'lpp': UTCDateTime(2016, 1, 24, 10, 37, 15, 150000), 'snr': None, 'epp': UTCDateTime(2016, 1, 24, 10, 36, 43, 150000), 'mpp': UTCDateTime(2016, 1, 24, 10, 36, 59, 150000), 'fm': None, 'spe': None, 'channel': u'LHE'}} expected = {
'P': {'picker': 'auto', 'snrdb': None, 'weight': 9, 'Mo': None, 'marked': 'shortsignallength', 'Mw': None,
'fc': None, 'snr': None, 'mpp': UTCDateTime(2016, 1, 24, 10, 36, 59, 150000), 'w0': None, 'spe': None,
'network': u'GR', 'epp': UTCDateTime(2016, 1, 24, 10, 36, 43, 150000),
'lpp': UTCDateTime(2016, 1, 24, 10, 37, 15, 150000), 'fm': 'N', 'channel': u'LHZ'},
'S': {'picker': 'auto', 'snrdb': None, 'network': u'GR', 'weight': 4, 'Ao': None,
'lpp': UTCDateTime(2016, 1, 24, 10, 37, 15, 150000), 'snr': None,
'epp': UTCDateTime(2016, 1, 24, 10, 36, 43, 150000),
'mpp': UTCDateTime(2016, 1, 24, 10, 36, 59, 150000), 'fm': None, 'spe': None, 'channel': u'LHE'}}
with HidePrints(): with HidePrints():
result, station = autopickstation(wfstream=self.gra2, pickparam=self.pickparam_taupy_disabled, metadata=(None, None)) result, station = autopickstation(wfstream=self.gra2, pickparam=self.pickparam_taupy_disabled,
metadata=(None, None))
self.assertDictContainsSubset(expected=expected['P'], actual=result['P']) self.assertDictContainsSubset(expected=expected['P'], actual=result['P'])
self.assertDictContainsSubset(expected=expected['S'], actual=result['S']) self.assertDictContainsSubset(expected=expected['S'], actual=result['S'])
self.assertEqual('GRA2', station) self.assertEqual('GRA2', station)
def test_autopickstation_taupy_enabled_gra2(self): def test_autopickstation_taupy_enabled_gra2(self):
expected = {'P': {'picker': 'auto', 'snrdb': 13.957959025719253, 'weight': 0, 'Mo': None, 'marked': [], 'Mw': None, 'fc': None, 'snr': 24.876879503607871, 'mpp': UTCDateTime(2016, 1, 24, 10, 41, 29, 150000), 'w0': None, 'spe': 1.0, 'network': u'GR', 'epp': UTCDateTime(2016, 1, 24, 10, 41, 26, 150000), 'lpp': UTCDateTime(2016, 1, 24, 10, 41, 30, 150000), 'fm': None, 'channel': u'LHZ'}, 'S': {'picker': 'auto', 'snrdb': 10.573236990555648, 'network': u'GR', 'weight': 1, 'Ao': None, 'lpp': UTCDateTime(2016, 1, 24, 10, 50, 34, 150000), 'snr': 11.410999834108294, 'epp': UTCDateTime(2016, 1, 24, 10, 50, 21, 150000), 'mpp': UTCDateTime(2016, 1, 24, 10, 50, 33, 150000), 'fm': None, 'spe': 4.666666666666667, 'channel': u'LHE'}} expected = {
'P': {'picker': 'auto', 'snrdb': 13.957959025719253, 'weight': 0, 'Mo': None, 'marked': [], 'Mw': None,
'fc': None, 'snr': 24.876879503607871, 'mpp': UTCDateTime(2016, 1, 24, 10, 41, 29, 150000),
'w0': None, 'spe': 1.0, 'network': u'GR', 'epp': UTCDateTime(2016, 1, 24, 10, 41, 26, 150000),
'lpp': UTCDateTime(2016, 1, 24, 10, 41, 30, 150000), 'fm': None, 'channel': u'LHZ'},
'S': {'picker': 'auto', 'snrdb': 10.573236990555648, 'network': u'GR', 'weight': 1, 'Ao': None,
'lpp': UTCDateTime(2016, 1, 24, 10, 50, 34, 150000), 'snr': 11.410999834108294,
'epp': UTCDateTime(2016, 1, 24, 10, 50, 21, 150000),
'mpp': UTCDateTime(2016, 1, 24, 10, 50, 33, 150000), 'fm': None, 'spe': 4.666666666666667,
'channel': u'LHE'}}
with HidePrints(): with HidePrints():
result, station = autopickstation(wfstream=self.gra2, pickparam=self.pickparam_taupy_enabled, metadata=self.metadata, origin = self.origin) result, station = autopickstation(wfstream=self.gra2, pickparam=self.pickparam_taupy_enabled,
metadata=self.metadata, origin=self.origin)
self.assertDictContainsSubset(expected=expected['P'], actual=result['P']) self.assertDictContainsSubset(expected=expected['P'], actual=result['P'])
self.assertDictContainsSubset(expected=expected['S'], actual=result['S']) self.assertDictContainsSubset(expected=expected['S'], actual=result['S'])
self.assertEqual('GRA2', station) self.assertEqual('GRA2', station)
def test_autopickstation_taupy_disabled_ech(self): def test_autopickstation_taupy_disabled_ech(self):
expected = {'P': {'picker': 'auto', 'snrdb': None, 'weight': 9, 'Mo': None, 'marked': 'SinsteadP', 'Mw': None, 'fc': None, 'snr': None, 'mpp': UTCDateTime(2016, 1, 24, 10, 26, 57), 'w0': None, 'spe': None, 'network': u'G', 'epp': UTCDateTime(2016, 1, 24, 10, 26, 41), 'lpp': UTCDateTime(2016, 1, 24, 10, 27, 13), 'fm': 'N', 'channel': u'LHZ'}, 'S': {'picker': 'auto', 'snrdb': None, 'network': u'G', 'weight': 4, 'Ao': None, 'lpp': UTCDateTime(2016, 1, 24, 10, 27, 13), 'snr': None, 'epp': UTCDateTime(2016, 1, 24, 10, 26, 41), 'mpp': UTCDateTime(2016, 1, 24, 10, 26, 57), 'fm': None, 'spe': None, 'channel': u'LHE'}} expected = {'P': {'picker': 'auto', 'snrdb': None, 'weight': 9, 'Mo': None, 'marked': 'SinsteadP', 'Mw': None,
'fc': None, 'snr': None, 'mpp': UTCDateTime(2016, 1, 24, 10, 26, 57), 'w0': None, 'spe': None,
'network': u'G', 'epp': UTCDateTime(2016, 1, 24, 10, 26, 41),
'lpp': UTCDateTime(2016, 1, 24, 10, 27, 13), 'fm': 'N', 'channel': u'LHZ'},
'S': {'picker': 'auto', 'snrdb': None, 'network': u'G', 'weight': 4, 'Ao': None,
'lpp': UTCDateTime(2016, 1, 24, 10, 27, 13), 'snr': None,
'epp': UTCDateTime(2016, 1, 24, 10, 26, 41), 'mpp': UTCDateTime(2016, 1, 24, 10, 26, 57),
'fm': None, 'spe': None, 'channel': u'LHE'}}
with HidePrints(): with HidePrints():
result, station = autopickstation(wfstream=self.ech, pickparam=self.pickparam_taupy_disabled) result, station = autopickstation(wfstream=self.ech, pickparam=self.pickparam_taupy_disabled)
self.assertDictContainsSubset(expected=expected['P'], actual=result['P']) self.assertDictContainsSubset(expected=expected['P'], actual=result['P'])
@@ -146,16 +196,32 @@ class TestAutopickStation(unittest.TestCase):
def test_autopickstation_taupy_enabled_ech(self): def test_autopickstation_taupy_enabled_ech(self):
# this station has a long time of before the first onset, so taupy will help during picking # this station has a long time of before the first onset, so taupy will help during picking
expected = {'P': {'picker': 'auto', 'snrdb': 9.9753586609166316, 'weight': 0, 'Mo': None, 'marked': [], 'Mw': None, 'fc': None, 'snr': 9.9434218804137107, 'mpp': UTCDateTime(2016, 1, 24, 10, 41, 34), 'w0': None, 'spe': 1.6666666666666667, 'network': u'G', 'epp': UTCDateTime(2016, 1, 24, 10, 41, 29), 'lpp': UTCDateTime(2016, 1, 24, 10, 41, 35), 'fm': None, 'channel': u'LHZ'}, 'S': {'picker': 'auto', 'snrdb': 12.698999454169567, 'network': u'G', 'weight': 0, 'Ao': None, 'lpp': UTCDateTime(2016, 1, 24, 10, 50, 44), 'snr': 18.616581906366577, 'epp': UTCDateTime(2016, 1, 24, 10, 50, 33), 'mpp': UTCDateTime(2016, 1, 24, 10, 50, 43), 'fm': None, 'spe': 3.3333333333333335, 'channel': u'LHE'}} expected = {
'P': {'picker': 'auto', 'snrdb': 9.9753586609166316, 'weight': 0, 'Mo': None, 'marked': [], 'Mw': None,
'fc': None, 'snr': 9.9434218804137107, 'mpp': UTCDateTime(2016, 1, 24, 10, 41, 34), 'w0': None,
'spe': 1.6666666666666667, 'network': u'G', 'epp': UTCDateTime(2016, 1, 24, 10, 41, 29),
'lpp': UTCDateTime(2016, 1, 24, 10, 41, 35), 'fm': None, 'channel': u'LHZ'},
'S': {'picker': 'auto', 'snrdb': 12.698999454169567, 'network': u'G', 'weight': 0, 'Ao': None,
'lpp': UTCDateTime(2016, 1, 24, 10, 50, 44), 'snr': 18.616581906366577,
'epp': UTCDateTime(2016, 1, 24, 10, 50, 33), 'mpp': UTCDateTime(2016, 1, 24, 10, 50, 43), 'fm': None,
'spe': 3.3333333333333335, 'channel': u'LHE'}}
with HidePrints(): with HidePrints():
result, station = autopickstation(wfstream=self.ech, pickparam=self.pickparam_taupy_enabled, metadata=self.metadata, origin=self.origin) result, station = autopickstation(wfstream=self.ech, pickparam=self.pickparam_taupy_enabled,
metadata=self.metadata, origin=self.origin)
self.assertDictContainsSubset(expected=expected['P'], actual=result['P']) self.assertDictContainsSubset(expected=expected['P'], actual=result['P'])
self.assertDictContainsSubset(expected=expected['S'], actual=result['S']) self.assertDictContainsSubset(expected=expected['S'], actual=result['S'])
self.assertEqual('ECH', station) self.assertEqual('ECH', station)
def test_autopickstation_taupy_disabled_fiesa(self): def test_autopickstation_taupy_disabled_fiesa(self):
# this station has a long time of before the first onset, so taupy will help during picking # this station has a long time of before the first onset, so taupy will help during picking
expected = {'P': {'picker': 'auto', 'snrdb': None, 'weight': 9, 'Mo': None, 'marked': 'SinsteadP', 'Mw': None, 'fc': None, 'snr': None, 'mpp': UTCDateTime(2016, 1, 24, 10, 35, 58), 'w0': None, 'spe': None, 'network': u'CH', 'epp': UTCDateTime(2016, 1, 24, 10, 35, 42), 'lpp': UTCDateTime(2016, 1, 24, 10, 36, 14), 'fm': 'N', 'channel': u'LHZ'}, 'S': {'picker': 'auto', 'snrdb': None, 'network': u'CH', 'weight': 4, 'Ao': None, 'lpp': UTCDateTime(2016, 1, 24, 10, 36, 14), 'snr': None, 'epp': UTCDateTime(2016, 1, 24, 10, 35, 42), 'mpp': UTCDateTime(2016, 1, 24, 10, 35, 58), 'fm': None, 'spe': None, 'channel': u'LHE'}} expected = {'P': {'picker': 'auto', 'snrdb': None, 'weight': 9, 'Mo': None, 'marked': 'SinsteadP', 'Mw': None,
'fc': None, 'snr': None, 'mpp': UTCDateTime(2016, 1, 24, 10, 35, 58), 'w0': None, 'spe': None,
'network': u'CH', 'epp': UTCDateTime(2016, 1, 24, 10, 35, 42),
'lpp': UTCDateTime(2016, 1, 24, 10, 36, 14), 'fm': 'N', 'channel': u'LHZ'},
'S': {'picker': 'auto', 'snrdb': None, 'network': u'CH', 'weight': 4, 'Ao': None,
'lpp': UTCDateTime(2016, 1, 24, 10, 36, 14), 'snr': None,
'epp': UTCDateTime(2016, 1, 24, 10, 35, 42), 'mpp': UTCDateTime(2016, 1, 24, 10, 35, 58),
'fm': None, 'spe': None, 'channel': u'LHE'}}
with HidePrints(): with HidePrints():
result, station = autopickstation(wfstream=self.fiesa, pickparam=self.pickparam_taupy_disabled) result, station = autopickstation(wfstream=self.fiesa, pickparam=self.pickparam_taupy_disabled)
self.assertDictContainsSubset(expected=expected['P'], actual=result['P']) self.assertDictContainsSubset(expected=expected['P'], actual=result['P'])
@@ -164,9 +230,18 @@ class TestAutopickStation(unittest.TestCase):
def test_autopickstation_taupy_enabled_fiesa(self): def test_autopickstation_taupy_enabled_fiesa(self):
# this station has a long time of before the first onset, so taupy will help during picking # this station has a long time of before the first onset, so taupy will help during picking
expected = {'P': {'picker': 'auto', 'snrdb': 13.921049277904373, 'weight': 0, 'Mo': None, 'marked': [], 'Mw': None, 'fc': None, 'snr': 24.666352170589487, 'mpp': UTCDateTime(2016, 1, 24, 10, 41, 47), 'w0': None, 'spe': 1.2222222222222285, 'network': u'CH', 'epp': UTCDateTime(2016, 1, 24, 10, 41, 43, 333333), 'lpp': UTCDateTime(2016, 1, 24, 10, 41, 48), 'fm': None, 'channel': u'LHZ'}, 'S': {'picker': 'auto', 'snrdb': 10.893086316477728, 'network': u'CH', 'weight': 0, 'Ao': None, 'lpp': UTCDateTime(2016, 1, 24, 10, 51, 5), 'snr': 12.283118216397849, 'epp': UTCDateTime(2016, 1, 24, 10, 50, 59, 333333), 'mpp': UTCDateTime(2016, 1, 24, 10, 51, 2), 'fm': None, 'spe': 2.8888888888888764, 'channel': u'LHE'}} expected = {
'P': {'picker': 'auto', 'snrdb': 13.921049277904373, 'weight': 0, 'Mo': None, 'marked': [], 'Mw': None,
'fc': None, 'snr': 24.666352170589487, 'mpp': UTCDateTime(2016, 1, 24, 10, 41, 47), 'w0': None,
'spe': 1.2222222222222285, 'network': u'CH', 'epp': UTCDateTime(2016, 1, 24, 10, 41, 43, 333333),
'lpp': UTCDateTime(2016, 1, 24, 10, 41, 48), 'fm': None, 'channel': u'LHZ'},
'S': {'picker': 'auto', 'snrdb': 10.893086316477728, 'network': u'CH', 'weight': 0, 'Ao': None,
'lpp': UTCDateTime(2016, 1, 24, 10, 51, 5), 'snr': 12.283118216397849,
'epp': UTCDateTime(2016, 1, 24, 10, 50, 59, 333333), 'mpp': UTCDateTime(2016, 1, 24, 10, 51, 2),
'fm': None, 'spe': 2.8888888888888764, 'channel': u'LHE'}}
with HidePrints(): with HidePrints():
result, station = autopickstation(wfstream=self.fiesa, pickparam=self.pickparam_taupy_enabled, metadata=self.metadata, origin=self.origin) result, station = autopickstation(wfstream=self.fiesa, pickparam=self.pickparam_taupy_enabled,
metadata=self.metadata, origin=self.origin)
self.assertDictContainsSubset(expected=expected['P'], actual=result['P']) self.assertDictContainsSubset(expected=expected['P'], actual=result['P'])
self.assertDictContainsSubset(expected=expected['S'], actual=result['S']) self.assertDictContainsSubset(expected=expected['S'], actual=result['S'])
self.assertEqual('FIESA', station) self.assertEqual('FIESA', station)
@@ -176,7 +251,8 @@ class TestAutopickStation(unittest.TestCase):
wfstream = self.gra1.copy() wfstream = self.gra1.copy()
wfstream = wfstream.select(channel='*E') + wfstream.select(channel='*N') wfstream = wfstream.select(channel='*E') + wfstream.select(channel='*N')
with HidePrints(): with HidePrints():
result, station = autopickstation(wfstream=wfstream, pickparam=self.pickparam_taupy_disabled, metadata=(None, None)) result, station = autopickstation(wfstream=wfstream, pickparam=self.pickparam_taupy_disabled,
metadata=(None, None))
self.assertIsNone(result) self.assertIsNone(result)
self.assertEqual('GRA1', station) self.assertEqual('GRA1', station)
@@ -184,17 +260,36 @@ class TestAutopickStation(unittest.TestCase):
"""Picking on a stream without horizontal traces should still pick the P phase on the vertical component""" """Picking on a stream without horizontal traces should still pick the P phase on the vertical component"""
wfstream = self.gra1.copy() wfstream = self.gra1.copy()
wfstream = wfstream.select(channel='*Z') wfstream = wfstream.select(channel='*Z')
expected = {'P': {'picker': 'auto', 'snrdb': 15.405649120980094, 'network': u'GR', 'weight': 0, 'Ao': None, 'Mo': None, 'marked': [], 'lpp': UTCDateTime(2016, 1, 24, 10, 41, 32, 690000), 'Mw': None, 'fc': None, 'snr': 34.718816470730317, 'epp': UTCDateTime(2016, 1, 24, 10, 41, 28, 890000), 'mpp': UTCDateTime(2016, 1, 24, 10, 41, 31, 690000), 'w0': None, 'spe': 0.9333333333333323, 'fm': 'D', 'channel': u'LHZ'}, 'S': {'picker': 'auto', 'snrdb': None, 'network': None, 'weight': 4, 'Mo': None, 'Ao': None, 'lpp': None, 'Mw': None, 'fc': None, 'snr': None, 'marked': [], 'mpp': None, 'w0': None, 'spe': None, 'epp': None, 'fm': 'N', 'channel': None}} expected = {
'P': {'picker': 'auto', 'snrdb': 15.405649120980094, 'network': u'GR', 'weight': 0, 'Ao': None, 'Mo': None,
'marked': [], 'lpp': UTCDateTime(2016, 1, 24, 10, 41, 32, 690000), 'Mw': None, 'fc': None,
'snr': 34.718816470730317, 'epp': UTCDateTime(2016, 1, 24, 10, 41, 28, 890000),
'mpp': UTCDateTime(2016, 1, 24, 10, 41, 31, 690000), 'w0': None, 'spe': 0.9333333333333323, 'fm': 'D',
'channel': u'LHZ'},
'S': {'picker': 'auto', 'snrdb': None, 'network': None, 'weight': 4, 'Mo': None, 'Ao': None, 'lpp': None,
'Mw': None, 'fc': None, 'snr': None, 'marked': [], 'mpp': None, 'w0': None, 'spe': None, 'epp': None,
'fm': 'N', 'channel': None}}
with HidePrints(): with HidePrints():
result, station = autopickstation(wfstream=wfstream, pickparam=self.pickparam_taupy_disabled, metadata=(None, None)) result, station = autopickstation(wfstream=wfstream, pickparam=self.pickparam_taupy_disabled,
metadata=(None, None))
self.assertEqual(expected, result) self.assertEqual(expected, result)
self.assertEqual('GRA1', station) self.assertEqual('GRA1', station)
def test_autopickstation_a106_taupy_enabled(self): def test_autopickstation_a106_taupy_enabled(self):
"""This station has invalid values recorded on both N and E component, but a pick can still be found on Z""" """This station has invalid values recorded on both N and E component, but a pick can still be found on Z"""
expected = {'P': {'picker': 'auto', 'snrdb': 12.862128789922826, 'network': u'Z3', 'weight': 0, 'Ao': None, 'Mo': None, 'marked': [], 'lpp': UTCDateTime(2016, 1, 24, 10, 41, 34), 'Mw': None, 'fc': None, 'snr': 19.329155459132608, 'epp': UTCDateTime(2016, 1, 24, 10, 41, 30), 'mpp': UTCDateTime(2016, 1, 24, 10, 41, 33), 'w0': None, 'spe': 1.6666666666666667, 'fm': None, 'channel': u'LHZ'}, 'S': {'picker': 'auto', 'snrdb': None, 'network': u'Z3', 'weight': 4, 'Ao': None, 'Mo': None, 'marked': [], 'lpp': UTCDateTime(2016, 1, 24, 10, 28, 56), 'Mw': None, 'fc': None, 'snr': None, 'epp': UTCDateTime(2016, 1, 24, 10, 28, 24), 'mpp': UTCDateTime(2016, 1, 24, 10, 28, 40), 'w0': None, 'spe': None, 'fm': None, 'channel': u'LHE'}} expected = {
'P': {'picker': 'auto', 'snrdb': 12.862128789922826, 'network': u'Z3', 'weight': 0, 'Ao': None, 'Mo': None,
'marked': [], 'lpp': UTCDateTime(2016, 1, 24, 10, 41, 34), 'Mw': None, 'fc': None,
'snr': 19.329155459132608, 'epp': UTCDateTime(2016, 1, 24, 10, 41, 30),
'mpp': UTCDateTime(2016, 1, 24, 10, 41, 33), 'w0': None, 'spe': 1.6666666666666667, 'fm': None,
'channel': u'LHZ'},
'S': {'picker': 'auto', 'snrdb': None, 'network': u'Z3', 'weight': 4, 'Ao': None, 'Mo': None, 'marked': [],
'lpp': UTCDateTime(2016, 1, 24, 10, 28, 56), 'Mw': None, 'fc': None, 'snr': None,
'epp': UTCDateTime(2016, 1, 24, 10, 28, 24), 'mpp': UTCDateTime(2016, 1, 24, 10, 28, 40), 'w0': None,
'spe': None, 'fm': None, 'channel': u'LHE'}}
with HidePrints(): with HidePrints():
result, station = autopickstation(wfstream=self.a106, pickparam=self.pickparam_taupy_enabled, metadata=self.metadata, origin=self.origin) result, station = autopickstation(wfstream=self.a106, pickparam=self.pickparam_taupy_enabled,
metadata=self.metadata, origin=self.origin)
self.assertEqual(expected, result) self.assertEqual(expected, result)
def test_autopickstation_station_missing_in_metadata(self): def test_autopickstation_station_missing_in_metadata(self):
@@ -202,10 +297,22 @@ class TestAutopickStation(unittest.TestCase):
relative to the theoretical onset to one relative to the traces starttime, eg never negative. relative to the theoretical onset to one relative to the traces starttime, eg never negative.
""" """
self.pickparam_taupy_enabled.setParamKV('pstart', -100) # modify starttime to be relative to theoretical onset self.pickparam_taupy_enabled.setParamKV('pstart', -100) # modify starttime to be relative to theoretical onset
expected = {'P': {'picker': 'auto', 'snrdb': 14.464757855513506, 'network': u'Z3', 'weight': 0, 'Mo': None, 'Ao': None, 'lpp': UTCDateTime(2016, 1, 24, 10, 41, 39, 605000), 'Mw': None, 'fc': None, 'snr': 27.956048519707181, 'marked': [], 'mpp': UTCDateTime(2016, 1, 24, 10, 41, 38, 605000), 'w0': None, 'spe': 1.6666666666666667, 'epp': UTCDateTime(2016, 1, 24, 10, 41, 35, 605000), 'fm': None, 'channel': u'LHZ'}, 'S': {'picker': 'auto', 'snrdb': 10.112844176301248, 'network': u'Z3', 'weight': 1, 'Mo': None, 'Ao': None, 'lpp': UTCDateTime(2016, 1, 24, 10, 50, 51, 605000), 'Mw': None, 'fc': None, 'snr': 10.263238413785425, 'marked': [], 'mpp': UTCDateTime(2016, 1, 24, 10, 50, 48, 605000), 'w0': None, 'spe': 4.666666666666667, 'epp': UTCDateTime(2016, 1, 24, 10, 50, 40, 605000), 'fm': None, 'channel': u'LHE'}} expected = {
'P': {'picker': 'auto', 'snrdb': 14.464757855513506, 'network': u'Z3', 'weight': 0, 'Mo': None, 'Ao': None,
'lpp': UTCDateTime(2016, 1, 24, 10, 41, 39, 605000), 'Mw': None, 'fc': None,
'snr': 27.956048519707181, 'marked': [], 'mpp': UTCDateTime(2016, 1, 24, 10, 41, 38, 605000),
'w0': None, 'spe': 1.6666666666666667, 'epp': UTCDateTime(2016, 1, 24, 10, 41, 35, 605000),
'fm': None, 'channel': u'LHZ'},
'S': {'picker': 'auto', 'snrdb': 10.112844176301248, 'network': u'Z3', 'weight': 1, 'Mo': None, 'Ao': None,
'lpp': UTCDateTime(2016, 1, 24, 10, 50, 51, 605000), 'Mw': None, 'fc': None,
'snr': 10.263238413785425, 'marked': [], 'mpp': UTCDateTime(2016, 1, 24, 10, 50, 48, 605000),
'w0': None, 'spe': 4.666666666666667, 'epp': UTCDateTime(2016, 1, 24, 10, 50, 40, 605000), 'fm': None,
'channel': u'LHE'}}
with HidePrints(): with HidePrints():
result, station = autopickstation(wfstream = self.a005a, pickparam=self.pickparam_taupy_enabled, metadata=self.metadata, origin=self.origin) result, station = autopickstation(wfstream=self.a005a, pickparam=self.pickparam_taupy_enabled,
metadata=self.metadata, origin=self.origin)
self.assertEqual(expected, result) self.assertEqual(expected, result)
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()
+33
View File
@@ -0,0 +1,33 @@
import unittest
from pylot.core.io.phases import getQualitiesfromxml
class TestQualityFromXML(unittest.TestCase):
def setUp(self):
self.path = '.'
self.ErrorsP = [0.02, 0.04, 0.08, 0.16]
self.ErrorsS = [0.04, 0.08, 0.16, 0.32]
self.test0_result = [[0.0136956521739, 0.0126, 0.0101612903226, 0.00734848484849, 0.0135069444444,
0.00649659863946, 0.0129513888889, 0.0122747747748, 0.0119252873563, 0.0103947368421,
0.0092380952381, 0.00916666666667, 0.0104444444444, 0.0125333333333, 0.00904761904762,
0.00885714285714, 0.00911616161616, 0.0164166666667, 0.0128787878788, 0.0122756410256,
0.013653253667966917], [0.0239333333333, 0.0223791578953, 0.0217974304255],
[0.0504861111111, 0.0610833333333], [], [0.171029411765]], [
[0.0195, 0.0203623188406, 0.0212121212121, 0.0345833333333, 0.0196180555556,
0.0202536231884, 0.0200347222222, 0.0189, 0.0210763888889, 0.018275862069,
0.0213888888889, 0.0319791666667, 0.0205303030303, 0.0156388888889, 0.0192,
0.0231349206349, 0.023625, 0.02875, 0.0195512820513, 0.0239393939394, 0.0234166666667,
0.0174702380952, 0.0204151307995], [0.040314343081226646], [0.148555555556], [], []]
self.test1_result = [77.77777777777777, 11.11111111111111, 7.407407407407407, 0, 3.7037037037037037],\
[92.0, 4.0, 4.0, 0, 0]
def test_result_plotflag0(self):
self.assertEqual(getQualitiesfromxml(self.path, self.ErrorsP, self.ErrorsS, 0), self.test0_result)
def test_result_plotflag1(self):
self.assertEqual(getQualitiesfromxml(self.path, self.ErrorsP, self.ErrorsS, 1), self.test1_result)
if __name__ == '__main__':
unittest.main()
+2
View File
@@ -1,4 +1,5 @@
import unittest import unittest
from pylot.core.pick.utils import get_quality_class from pylot.core.pick.utils import get_quality_class
@@ -52,5 +53,6 @@ class TestQualityClassFromUncertainty(unittest.TestCase):
# Error exactly in class 3 # Error exactly in class 3
self.assertEqual(3, get_quality_class(5.6, self.error_classes)) self.assertEqual(3, get_quality_class(5.6, self.error_classes))
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()
+1
View File
@@ -33,6 +33,7 @@ class HidePrints:
def silencer(*args, **kwargs): def silencer(*args, **kwargs):
with HidePrints(): with HidePrints():
func(*args, **kwargs) func(*args, **kwargs)
return silencer return silencer
def __init__(self, hide_prints=True): def __init__(self, hide_prints=True):