Compare commits
161
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e434bda993 | ||
|
|
c0c3cbbd7b | ||
|
|
76d4ec290c | ||
|
|
63dac0fff6 | ||
|
|
b15cfe2e1d | ||
|
|
29a1e4ebe6 | ||
|
|
50cabb0038 | ||
|
|
8dd5789b0c | ||
|
|
c4aeab0d89 | ||
|
|
ef69fc429f | ||
|
|
c8f9c1c33a | ||
|
|
59f2c4b46f | ||
|
|
8eb958c91b | ||
|
|
6b7f297d7a | ||
|
|
70d5c2d621 | ||
|
|
7393201b90 | ||
|
|
d02f74ab10 | ||
|
|
0a5f5f0817 | ||
|
|
db976e5ea9 | ||
|
|
c021ca19d7 | ||
|
|
356988e71d | ||
|
|
ad62284e0e | ||
|
|
0fb0b0f11c | ||
|
|
e3dd4a4e28 | ||
|
|
2bbb84190c | ||
|
|
fb32d5e0c5 | ||
|
|
f043401cc0 | ||
|
|
eb15382b5f | ||
|
|
7fbc3bc5ae | ||
|
|
221743fe20 | ||
|
|
554afc5a81 | ||
|
|
c79e886d77 | ||
|
|
76f2d5d972 | ||
|
|
2d08fd029d | ||
|
|
8f22d438d3 | ||
|
|
93b7de3baa | ||
|
|
05642e775b | ||
|
|
47205ca493 | ||
|
|
5c7f0b56eb | ||
|
|
c574031931 | ||
|
|
e1a0fde619 | ||
|
|
48d196df11 | ||
|
|
6cc9cb4a96 | ||
|
|
5eab686445 | ||
|
|
b12e7937ac | ||
|
|
78f2dbcab2 | ||
|
|
8b95c7a0fe | ||
|
|
f03ace75e7 | ||
|
|
9c78471d20 | ||
|
|
09d2fb1022 | ||
|
|
3cae6d3a78 | ||
|
|
2e85d083a3 | ||
|
|
ba4e6cfe50 | ||
|
|
1f16d01648 | ||
|
|
3069e7d526 | ||
|
|
a9aeb7aaa3 | ||
|
|
b9adb182ad | ||
|
|
a823eb2440 | ||
|
|
486e3dc9c3 | ||
|
|
8d356050d7 | ||
|
|
43cab3767f | ||
|
|
a1f6c5ffca | ||
|
|
e4e7afa996 | ||
|
|
0634d24814 | ||
|
|
43c2b97b3d | ||
|
|
8d94440e77 | ||
|
|
66b7dea706 | ||
|
|
ebf6d4806a | ||
|
|
207d0b3a6f | ||
|
|
3b3bbc29d1 | ||
|
|
0c3fca9299 | ||
|
|
2d33a60421 | ||
|
|
a8c6f4c972 | ||
|
|
0d91f9e3fe | ||
|
|
494d281d61 | ||
|
|
5ef427ec12 | ||
|
|
29cf978782 | ||
|
|
091449819c | ||
|
|
cd9c139349 | ||
|
|
084fb10cea | ||
|
|
dde9520879 | ||
|
|
7847f40a35 | ||
|
|
2c188432a1 | ||
|
|
86dc0f5436 | ||
|
|
83ba63a3fd | ||
|
|
3392100206 | ||
|
|
401265eb3a | ||
|
|
dd685d5d5e | ||
|
|
3cd17ff364 | ||
|
|
d879aa1a8b | ||
|
|
445f1da5ac | ||
|
|
962cf4edac | ||
|
|
eb0dd87a9e | ||
|
|
e9cc579cf6 | ||
|
|
f1fd52b750 | ||
|
|
5449210797 | ||
|
|
0af948030b | ||
|
|
21b1be0e56 | ||
|
|
58eee13b07 | ||
|
|
c57eb4f556 | ||
|
|
47bb8c4326 | ||
|
|
29ffcf2e37 | ||
|
|
e35d5d6df9 | ||
|
|
79f3d40714 | ||
|
|
9cef22b74b | ||
|
|
c254ee09b3 | ||
|
|
be32f4f61d | ||
|
|
9036a9054e | ||
|
|
dd02527c1d | ||
|
|
a383101d2c | ||
|
|
7c926327dd | ||
|
|
e9d3caafd3 | ||
|
|
0b50a91f57 | ||
|
|
8725f5083c | ||
|
|
13e0d8b106 | ||
|
|
a13defbe42 | ||
|
|
0ee239f4a6 | ||
|
|
131f6782af | ||
|
|
15cad42868 | ||
|
|
0dc5aff3fc | ||
|
|
7f9d240d02 | ||
|
|
7c5e16ecc6 | ||
|
|
321a871d62 | ||
|
|
a738dc57ec | ||
|
|
ca56139922 | ||
|
|
81240a8ffe | ||
|
|
d236d3d80f | ||
|
|
2dd48b4c62 | ||
|
|
e850e5d4fe | ||
|
|
4fd27994f6 | ||
|
|
ca140d3fe1 | ||
|
|
60db23e83c | ||
|
|
b976ec6ab0 | ||
|
|
702b7f3fb1 | ||
|
|
320cb42390 | ||
|
|
cc1139d2ca | ||
|
|
aa73bb0ae8 | ||
|
|
de11c3b611 | ||
|
|
92c328e3c6 | ||
|
|
819ea8bf00 | ||
|
|
bd9c1d0f73 | ||
|
|
30eb8fbbbd | ||
|
|
8b03aab381 | ||
|
|
5efe75f4ea | ||
|
|
7ccad4cb86 | ||
|
|
3af02bbbf7 | ||
|
|
a6af015f05 | ||
|
|
b4e72eafe7 | ||
|
|
fbeab85bb3 | ||
|
|
0c43040faf | ||
|
|
6e40811b01 | ||
|
|
04067cac8e | ||
|
|
67e2ba3ad7 | ||
|
|
2ccdfb8470 | ||
|
|
f502ac0a00 | ||
|
|
978bf42a3e | ||
|
|
5289be55dd | ||
|
|
27c9bbb96a | ||
|
|
4451209907 | ||
|
|
ded46d45cc | ||
|
|
c9bd0a147c |
@@ -1,3 +1,4 @@
|
|||||||
*.pyc
|
*.pyc
|
||||||
*~
|
*~
|
||||||
|
.idea
|
||||||
pylot/RELEASE-VERSION
|
pylot/RELEASE-VERSION
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
+15
-18
@@ -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
|
||||||
|
|
||||||
@@ -27,7 +28,7 @@ from pylot.core.util.dataprocessing import restitute_data, Metadata
|
|||||||
from pylot.core.util.defaults import SEPARATOR
|
from pylot.core.util.defaults import SEPARATOR
|
||||||
from pylot.core.util.event import Event
|
from pylot.core.util.event import Event
|
||||||
from pylot.core.util.structure import DATASTRUCTURE
|
from pylot.core.util.structure import DATASTRUCTURE
|
||||||
from pylot.core.util.utils import get_None, trim_station_components, check4gapsAndRemove, check4doubled, \
|
from pylot.core.util.utils import get_none, trim_station_components, check4gapsAndRemove, check4doubled, \
|
||||||
check4rotated
|
check4rotated
|
||||||
from pylot.core.util.version import get_git_version as _getVersionString
|
from pylot.core.util.version import get_git_version as _getVersionString
|
||||||
|
|
||||||
@@ -90,9 +91,9 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even
|
|||||||
sp=sp_info)
|
sp=sp_info)
|
||||||
print(splash)
|
print(splash)
|
||||||
|
|
||||||
parameter = get_None(parameter)
|
parameter = get_none(parameter)
|
||||||
inputfile = get_None(inputfile)
|
inputfile = get_none(inputfile)
|
||||||
eventid = get_None(eventid)
|
eventid = get_none(eventid)
|
||||||
|
|
||||||
fig_dict = None
|
fig_dict = None
|
||||||
fig_dict_wadatijack = None
|
fig_dict_wadatijack = None
|
||||||
@@ -118,13 +119,9 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even
|
|||||||
obspyDMT_wfpath = input_dict['obspyDMT_wfpath']
|
obspyDMT_wfpath = input_dict['obspyDMT_wfpath']
|
||||||
|
|
||||||
if not parameter:
|
if not parameter:
|
||||||
if inputfile:
|
if not inputfile:
|
||||||
|
print('Using default input parameter')
|
||||||
parameter = PylotParameter(inputfile)
|
parameter = PylotParameter(inputfile)
|
||||||
# iplot = parameter['iplot']
|
|
||||||
else:
|
|
||||||
infile = os.path.join(os.path.expanduser('~'), '.pylot', 'pylot.in')
|
|
||||||
print('Using default input file {}'.format(infile))
|
|
||||||
parameter = PylotParameter(infile)
|
|
||||||
else:
|
else:
|
||||||
if not type(parameter) == PylotParameter:
|
if not type(parameter) == PylotParameter:
|
||||||
print('Wrong input type for parameter: {}'.format(type(parameter)))
|
print('Wrong input type for parameter: {}'.format(type(parameter)))
|
||||||
@@ -145,7 +142,7 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even
|
|||||||
|
|
||||||
exf = ['root', 'dpath', 'dbase']
|
exf = ['root', 'dpath', 'dbase']
|
||||||
|
|
||||||
if parameter['eventID'] is not '*' and fnames == 'None':
|
if parameter['eventID'] != '*' and fnames == 'None':
|
||||||
dsfields['eventID'] = parameter['eventID']
|
dsfields['eventID'] = parameter['eventID']
|
||||||
exf.append('eventID')
|
exf.append('eventID')
|
||||||
|
|
||||||
@@ -153,7 +150,7 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even
|
|||||||
datastructure.setExpandFields(exf)
|
datastructure.setExpandFields(exf)
|
||||||
|
|
||||||
# check if default location routine NLLoc is available and all stations are used
|
# check if default location routine NLLoc is available and all stations are used
|
||||||
if get_None(parameter['nllocbin']) and station == 'all':
|
if get_none(parameter['nllocbin']) and station == 'all':
|
||||||
locflag = 1
|
locflag = 1
|
||||||
# get NLLoc-root path
|
# get NLLoc-root path
|
||||||
nllocroot = parameter.get('nllocroot')
|
nllocroot = parameter.get('nllocroot')
|
||||||
@@ -189,12 +186,12 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even
|
|||||||
if not input_dict:
|
if not input_dict:
|
||||||
# started in production mode
|
# started in production mode
|
||||||
datapath = datastructure.expandDataPath()
|
datapath = datastructure.expandDataPath()
|
||||||
if fnames == 'None' and parameter['eventID'] is '*':
|
if fnames == 'None' and parameter['eventID'] == '*':
|
||||||
# multiple event processing
|
# multiple event processing
|
||||||
# read each event in database
|
# read each event in database
|
||||||
events = [event for event in glob.glob(os.path.join(datapath, '*')) if
|
events = [event for event in glob.glob(os.path.join(datapath, '*')) if
|
||||||
(os.path.isdir(event) and not event.endswith('EVENTS-INFO'))]
|
(os.path.isdir(event) and not event.endswith('EVENTS-INFO'))]
|
||||||
elif fnames == 'None' and parameter['eventID'] is not '*' and not type(parameter['eventID']) == list:
|
elif fnames == 'None' and parameter['eventID'] != '*' and not type(parameter['eventID']) == list:
|
||||||
# single event processing
|
# single event processing
|
||||||
events = glob.glob(os.path.join(datapath, parameter['eventID']))
|
events = glob.glob(os.path.join(datapath, parameter['eventID']))
|
||||||
elif fnames == 'None' and type(parameter['eventID']) == list:
|
elif fnames == 'None' and type(parameter['eventID']) == list:
|
||||||
@@ -246,7 +243,7 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even
|
|||||||
pylot_event = Event(eventpath) # event should be path to event directory
|
pylot_event = Event(eventpath) # event should be path to event directory
|
||||||
data.setEvtData(pylot_event)
|
data.setEvtData(pylot_event)
|
||||||
if fnames == 'None':
|
if fnames == 'None':
|
||||||
data.setWFData(glob.glob(os.path.join(datapath, event_datapath, '*')))
|
data.set_wf_data(glob.glob(os.path.join(datapath, event_datapath, '*')))
|
||||||
# the following is necessary because within
|
# the following is necessary because within
|
||||||
# multiple event processing no event ID is provided
|
# multiple event processing no event ID is provided
|
||||||
# in autopylot.in
|
# in autopylot.in
|
||||||
@@ -261,7 +258,7 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even
|
|||||||
now.minute)
|
now.minute)
|
||||||
parameter.setParam(eventID=eventID)
|
parameter.setParam(eventID=eventID)
|
||||||
else:
|
else:
|
||||||
data.setWFData(fnames)
|
data.set_wf_data(fnames)
|
||||||
|
|
||||||
eventpath = events[0]
|
eventpath = events[0]
|
||||||
# now = datetime.datetime.now()
|
# now = datetime.datetime.now()
|
||||||
@@ -271,13 +268,13 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even
|
|||||||
# now.hour,
|
# now.hour,
|
||||||
# now.minute)
|
# now.minute)
|
||||||
parameter.setParam(eventID=eventid)
|
parameter.setParam(eventID=eventid)
|
||||||
wfdat = data.getWFData() # all available streams
|
wfdat = data.get_wf_data() # all available streams
|
||||||
if not station == 'all':
|
if not station == 'all':
|
||||||
wfdat = wfdat.select(station=station)
|
wfdat = wfdat.select(station=station)
|
||||||
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)
|
||||||
|
|||||||
+5
-3
@@ -3,8 +3,10 @@
|
|||||||
#$ -l low
|
#$ -l low
|
||||||
#$ -cwd
|
#$ -cwd
|
||||||
#$ -pe smp 40
|
#$ -pe smp 40
|
||||||
#$ -l mem=2G
|
##$ -l mem=3G
|
||||||
#$ -l h_vmem=2G
|
#$ -l h_vmem=6G
|
||||||
#$ -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_311
|
||||||
|
|
||||||
|
python ./autoPyLoT.py -i /home/marcel/.pylot/pylot_adriaarray.in -c 20 -dmt processed
|
||||||
|
|||||||
+244
-145
@@ -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 | Description
|
| Icon | 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 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. |
|
||||||
 | 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.
|
|  | 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 waveform.
|
|  | - | - | Zoom into waveform. |
|
||||||
 | - | - | Reset zoom to default view.
|
|  | - | - | Reset zoom to default view. |
|
||||||
 | - | - | Delete all manual picks on this station.
|
|  | - | - | Delete all manual picks on this station. |
|
||||||
 | - | - | Click this button and then the picked phase to rename it.
|
|  | - | - | Click this button and then the picked phase to rename it. |
|
||||||
 | - | - | 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.
|
|  | - | - | 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
@@ -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. |
|
||||||
|
|
||||||
|
|||||||
@@ -30,6 +30,7 @@
|
|||||||
<file>icons/openloc.png</file>
|
<file>icons/openloc.png</file>
|
||||||
<file>icons/compare_button.png</file>
|
<file>icons/compare_button.png</file>
|
||||||
<file>icons/pick_qualities_button.png</file>
|
<file>icons/pick_qualities_button.png</file>
|
||||||
|
<file>icons/eventlist_xml_button.png</file>
|
||||||
<file>icons/locate_button.png</file>
|
<file>icons/locate_button.png</file>
|
||||||
<file>icons/Matlab_PILOT_icon.png</file>
|
<file>icons/Matlab_PILOT_icon.png</file>
|
||||||
<file>icons/printer.png</file>
|
<file>icons/printer.png</file>
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 3.2 KiB |
+1458
-7583
File diff suppressed because it is too large
Load Diff
+109776
-109085
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,14 @@
|
|||||||
|
name: pylot_38
|
||||||
|
channels:
|
||||||
|
- conda-forge
|
||||||
|
- defaults
|
||||||
|
dependencies:
|
||||||
|
- 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
|
||||||
@@ -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,7 +227,7 @@ 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]
|
||||||
@@ -282,12 +286,13 @@ class LocalMagnitude(Magnitude):
|
|||||||
for a in self.arrivals:
|
for a in self.arrivals:
|
||||||
if a.phase not in 'sS':
|
if a.phase not in 'sS':
|
||||||
continue
|
continue
|
||||||
|
pick = a.pick_id.get_referred_object()
|
||||||
|
station = pick.waveform_id.station_code
|
||||||
# make sure calculating Ml only from reliable onsets
|
# make sure calculating Ml only from reliable onsets
|
||||||
# NLLoc: time_weight = 0 => do not use onset!
|
# NLLoc: time_weight = 0 => do not use onset!
|
||||||
if a.time_weight == 0:
|
if a.time_weight == 0:
|
||||||
|
print("Uncertain pick at Station {}, do not use it!".format(station))
|
||||||
continue
|
continue
|
||||||
pick = a.pick_id.get_referred_object()
|
|
||||||
station = pick.waveform_id.station_code
|
|
||||||
wf = select_for_phase(self.stream.select(
|
wf = select_for_phase(self.stream.select(
|
||||||
station=station), a.phase)
|
station=station), a.phase)
|
||||||
if not wf:
|
if not wf:
|
||||||
@@ -399,24 +404,32 @@ class MomentMagnitude(Magnitude):
|
|||||||
print("WARNING: No instrument corrected data available,"
|
print("WARNING: No instrument corrected data available,"
|
||||||
" no magnitude calculation possible! Go on.")
|
" no magnitude calculation possible! Go on.")
|
||||||
continue
|
continue
|
||||||
scopy = self.stream.copy()
|
wf = self.stream.select(station=station)
|
||||||
wf = scopy.select(station=station)
|
|
||||||
if not wf:
|
if not wf:
|
||||||
continue
|
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
|
onset = pick.time
|
||||||
distance = degrees2kilometers(a.distance)
|
distance = degrees2kilometers(a.distance)
|
||||||
azimuth = a.azimuth
|
azimuth = a.azimuth
|
||||||
incidence = a.takeoff_angle
|
incidence = a.takeoff_angle
|
||||||
w0, fc = calcsourcespec(wf, onset, self.p_velocity, distance,
|
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,
|
||||||
azimuth, incidence, self.p_attenuation,
|
azimuth, incidence, self.p_attenuation,
|
||||||
self.plot_flag, self.verbose)
|
self.plot_flag, self.verbose)
|
||||||
if w0 is None or fc is None:
|
if w0 is None or fc is None:
|
||||||
if self.verbose:
|
if self.verbose:
|
||||||
print("WARNING: insufficient frequency information")
|
print("WARNING: insufficient frequency information")
|
||||||
continue
|
continue
|
||||||
WF = select_for_phase(self.stream.select(
|
WF = select_for_phase(scopy, "P")
|
||||||
station=station), a.phase)
|
|
||||||
WF = select_for_phase(WF, "P")
|
|
||||||
m0, mw = calcMoMw(WF, w0, self.rock_density, self.p_velocity,
|
m0, mw = calcMoMw(WF, w0, self.rock_density, self.p_velocity,
|
||||||
distance, self.verbose)
|
distance, self.verbose)
|
||||||
self.moment_props = (station, dict(w0=w0, fc=fc, Mo=m0))
|
self.moment_props = (station, dict(w0=w0, fc=fc, Mo=m0))
|
||||||
@@ -427,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):
|
||||||
'''
|
'''
|
||||||
@@ -589,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)
|
||||||
|
|
||||||
@@ -674,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()
|
||||||
|
|||||||
+41
-597
@@ -1,609 +1,69 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
import copy
|
|
||||||
import os
|
import os
|
||||||
from obspy import read_events
|
from dataclasses import dataclass, field
|
||||||
from obspy.core import read, Stream, UTCDateTime
|
from typing import List, Union
|
||||||
from obspy.core.event import Event as ObsPyEvent
|
|
||||||
from obspy.io.sac import SacIOError
|
|
||||||
|
|
||||||
from PySide.QtGui import QMessageBox
|
from obspy import UTCDateTime
|
||||||
|
|
||||||
import pylot.core.loc.velest as velest
|
from pylot.core.io.event import EventData
|
||||||
import pylot.core.loc.focmec as focmec
|
from pylot.core.io.waveformdata import WaveformData
|
||||||
import pylot.core.loc.hypodd as hypodd
|
from pylot.core.util.dataprocessing import Metadata
|
||||||
from pylot.core.io.phases import readPILOTEvent, picks_from_picksdict, \
|
|
||||||
picksdict_from_pilot, merge_picks, PylotParameter
|
|
||||||
from pylot.core.util.errors import FormatError, OverwriteError
|
|
||||||
from pylot.core.util.event import Event
|
|
||||||
from pylot.core.util.obspyDMT_interface import qml_from_obspyDMT
|
|
||||||
from pylot.core.util.utils import fnConstructor, full_range, check4rotated, \
|
|
||||||
check4gapsAndMerge, trim_station_components
|
|
||||||
|
|
||||||
class Data(object):
|
@dataclass
|
||||||
"""
|
class Data:
|
||||||
Data container with attributes wfdata holding ~obspy.core.stream.
|
event_data: EventData = field(default_factory=EventData)
|
||||||
|
waveform_data: WaveformData = field(default_factory=WaveformData)
|
||||||
:type parent: PySide.QtGui.QWidget object, optional
|
metadata: Metadata = field(default_factory=Metadata)
|
||||||
:param parent: A PySide.QtGui.QWidget object utilized when
|
_parent: Union[None, 'QtWidgets.QWidget'] = None
|
||||||
called by a GUI to display a PySide.QtGui.QMessageBox instead of printing
|
|
||||||
to standard out.
|
|
||||||
:type evtdata: ~obspy.core.event.Event object, optional
|
|
||||||
:param evtdata ~obspy.core.event.Event object containing all derived or
|
|
||||||
loaded event. Container object holding, e.g. phase arrivals, etc.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, parent=None, evtdata=None):
|
def __init__(self, parent=None, evtdata=None):
|
||||||
self._parent = parent
|
self._parent = parent
|
||||||
if self.getParent():
|
self.event_data = EventData(evtdata)
|
||||||
self.comp = parent.getComponent()
|
self.waveform_data = WaveformData()
|
||||||
else:
|
|
||||||
self.comp = 'Z'
|
|
||||||
self.wfdata = Stream()
|
|
||||||
self._new = False
|
|
||||||
if isinstance(evtdata, ObsPyEvent) or isinstance(evtdata, Event):
|
|
||||||
pass
|
|
||||||
elif isinstance(evtdata, dict):
|
|
||||||
evt = readPILOTEvent(**evtdata)
|
|
||||||
evtdata = evt
|
|
||||||
elif type(evtdata) in [str, unicode]:
|
|
||||||
try:
|
|
||||||
cat = read_events(evtdata)
|
|
||||||
if len(cat) is not 1:
|
|
||||||
raise ValueError('ambiguous event information for file: '
|
|
||||||
'{file}'.format(file=evtdata))
|
|
||||||
evtdata = cat[0]
|
|
||||||
except TypeError as e:
|
|
||||||
if 'Unknown format for file' in e.message:
|
|
||||||
if 'PHASES' in evtdata:
|
|
||||||
picks = picksdict_from_pilot(evtdata)
|
|
||||||
evtdata = ObsPyEvent()
|
|
||||||
evtdata.picks = picks_from_picksdict(picks)
|
|
||||||
elif 'LOC' in evtdata:
|
|
||||||
raise NotImplementedError('PILOT location information '
|
|
||||||
'read support not yet '
|
|
||||||
'implemeted.')
|
|
||||||
elif 'event.pkl' in evtdata:
|
|
||||||
evtdata = qml_from_obspyDMT(evtdata)
|
|
||||||
else:
|
|
||||||
raise e
|
|
||||||
else:
|
|
||||||
raise e
|
|
||||||
else: # create an empty Event object
|
|
||||||
self.setNew()
|
|
||||||
evtdata = ObsPyEvent()
|
|
||||||
evtdata.picks = []
|
|
||||||
self.evtdata = evtdata
|
|
||||||
self.wforiginal = None
|
|
||||||
self.cuttimes = None
|
|
||||||
self.dirty = False
|
|
||||||
self.processed = None
|
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return str(self.wfdata)
|
return str(self.waveform_data.wfdata)
|
||||||
|
|
||||||
def __add__(self, other):
|
def __add__(self, other):
|
||||||
assert isinstance(other, Data), "operands must be of same type 'Data'"
|
if not isinstance(other, Data):
|
||||||
rs_id = self.get_evt_data().get('resource_id')
|
raise TypeError("Operands must be of type 'Data'")
|
||||||
rs_id_other = other.get_evt_data().get('resource_id')
|
if self.event_data.is_new() and other.event_data.is_new():
|
||||||
if other.isNew() and not self.isNew():
|
|
||||||
picks_to_add = other.get_evt_data().picks
|
|
||||||
old_picks = self.get_evt_data().picks
|
|
||||||
wf_ids_old = [pick.waveform_id for pick in old_picks]
|
|
||||||
for new_pick in picks_to_add:
|
|
||||||
wf_id = new_pick.waveform_id
|
|
||||||
if wf_id in wf_ids_old:
|
|
||||||
for old_pick in old_picks:
|
|
||||||
comparison = [old_pick.waveform_id == new_pick.waveform_id,
|
|
||||||
old_pick.phase_hint == new_pick.phase_hint,
|
|
||||||
old_pick.method_id == new_pick.method_id]
|
|
||||||
if all(comparison):
|
|
||||||
del(old_pick)
|
|
||||||
old_picks.append(new_pick)
|
|
||||||
elif not other.isNew() and self.isNew():
|
|
||||||
new = other + self
|
|
||||||
self.evtdata = new.get_evt_data()
|
|
||||||
elif self.isNew() and other.isNew():
|
|
||||||
pass
|
pass
|
||||||
elif rs_id == rs_id_other:
|
elif other.event_data.is_new():
|
||||||
other.setNew()
|
new_picks = other.event_data.evtdata.picks
|
||||||
|
old_picks = self.event_data.evtdata.picks
|
||||||
|
old_picks.extend([pick for pick in new_picks if pick not in old_picks])
|
||||||
|
elif self.event_data.is_new():
|
||||||
|
return other + self
|
||||||
|
elif self.event_data.get_id() == other.event_data.get_id():
|
||||||
|
other.event_data.set_new()
|
||||||
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 get_parent(self):
|
||||||
"""
|
|
||||||
Return picks in event data
|
|
||||||
:return: picks seperated by newlines
|
|
||||||
:rtype: str
|
|
||||||
"""
|
|
||||||
picks_str = ''
|
|
||||||
for pick in self.get_evt_data().picks:
|
|
||||||
picks_str += str(pick) + '\n'
|
|
||||||
return picks_str
|
|
||||||
|
|
||||||
def getParent(self):
|
|
||||||
"""
|
|
||||||
Get PySide.QtGui.QWidget parent object
|
|
||||||
"""
|
|
||||||
return self._parent
|
return self._parent
|
||||||
|
|
||||||
def isNew(self):
|
def filter_wf_data(self, **kwargs):
|
||||||
return self._new
|
self.waveform_data.wfdata.detrend('linear')
|
||||||
|
self.waveform_data.wfdata.taper(0.02, type='cosine')
|
||||||
|
self.waveform_data.wfdata.filter(**kwargs)
|
||||||
|
self.waveform_data.dirty = True
|
||||||
|
|
||||||
def setNew(self):
|
def set_wf_data(self, fnames: List[str], fnames_alt: List[str] = None, check_rotated=False, metadata=None, tstart=0, tstop=0):
|
||||||
self._new = True
|
return self.waveform_data.load_waveforms(fnames, fnames_alt, check_rotated, metadata, tstart, tstop)
|
||||||
|
|
||||||
def getCutTimes(self):
|
def reset_wf_data(self):
|
||||||
"""
|
self.waveform_data.reset()
|
||||||
Returns earliest start and latest end of all waveform data
|
|
||||||
:return: minimum start time and maximum end time as a tuple
|
|
||||||
:rtype: (UTCDateTime, UTCDateTime)
|
|
||||||
"""
|
|
||||||
if self.cuttimes is None:
|
|
||||||
self.updateCutTimes()
|
|
||||||
return self.cuttimes
|
|
||||||
|
|
||||||
def updateCutTimes(self):
|
def get_wf_data(self):
|
||||||
"""
|
return self.waveform_data.wfdata
|
||||||
Update cuttimes to contain earliest start and latest end time
|
|
||||||
of all waveform data
|
|
||||||
:rtype: None
|
|
||||||
"""
|
|
||||||
self.cuttimes = full_range(self.getWFData())
|
|
||||||
|
|
||||||
def getEventFileName(self):
|
def rotate_wf_data(self):
|
||||||
ID = self.getID()
|
self.waveform_data.rotate_zne(self.metadata)
|
||||||
# handle forbidden filenames especially on windows systems
|
|
||||||
return fnConstructor(str(ID))
|
|
||||||
|
|
||||||
def checkEvent(self, event, fcheck, forceOverwrite=False):
|
|
||||||
"""
|
|
||||||
Check information in supplied event and own event and replace own
|
|
||||||
information with supplied information if own information not exiisting
|
|
||||||
or forced by forceOverwrite
|
|
||||||
:param event: Event that supplies information for comparison
|
|
||||||
:type event: pylot.core.util.event.Event
|
|
||||||
:param fcheck: check and delete existing information
|
|
||||||
can be a str or a list of strings of ['manual', 'auto', 'origin', 'magnitude']
|
|
||||||
:type fcheck: str, [str]
|
|
||||||
:param forceOverwrite: Set to true to force overwrite own information. If false,
|
|
||||||
supplied information from event is only used if there is no own information in that
|
|
||||||
category (given in fcheck: manual, auto, origin, magnitude)
|
|
||||||
:type forceOverwrite: bool
|
|
||||||
:return:
|
|
||||||
:rtype: None
|
|
||||||
"""
|
|
||||||
if 'origin' in fcheck:
|
|
||||||
self.replaceOrigin(event, forceOverwrite)
|
|
||||||
if 'magnitude' in fcheck:
|
|
||||||
self.replaceMagnitude(event, forceOverwrite)
|
|
||||||
if 'auto' in fcheck:
|
|
||||||
self.replacePicks(event, 'auto')
|
|
||||||
if 'manual' in fcheck:
|
|
||||||
self.replacePicks(event, 'manual')
|
|
||||||
|
|
||||||
def replaceOrigin(self, event, forceOverwrite=False):
|
|
||||||
"""
|
|
||||||
Replace own origin with the one supplied in event if own origin is not
|
|
||||||
existing or forced by forceOverwrite = True
|
|
||||||
:param event: Event that supplies information for comparison
|
|
||||||
:type event: pylot.core.util.event.Event
|
|
||||||
:param forceOverwrite: always replace own information with supplied one if true
|
|
||||||
:type forceOverwrite: bool
|
|
||||||
:return:
|
|
||||||
:rtype: None
|
|
||||||
"""
|
|
||||||
if self.get_evt_data().origins or forceOverwrite:
|
|
||||||
if event.origins:
|
|
||||||
print("Found origin, replace it by new origin.")
|
|
||||||
event.origins = self.get_evt_data().origins
|
|
||||||
|
|
||||||
def replaceMagnitude(self, event, forceOverwrite=False):
|
|
||||||
"""
|
|
||||||
Replace own magnitude with the one supplied in event if own magnitude is not
|
|
||||||
existing or forced by forceOverwrite = True
|
|
||||||
:param event: Event that supplies information for comparison
|
|
||||||
:type event: pylot.core.util.event.Event
|
|
||||||
:param forceOverwrite: always replace own information with supplied one if true
|
|
||||||
:type forceOverwrite: bool
|
|
||||||
:return:
|
|
||||||
:rtype: None
|
|
||||||
"""
|
|
||||||
if self.get_evt_data().magnitudes or forceOverwrite:
|
|
||||||
if event.magnitudes:
|
|
||||||
print("Found magnitude, replace it by new magnitude")
|
|
||||||
event.magnitudes = self.get_evt_data().magnitudes
|
|
||||||
|
|
||||||
def replacePicks(self, event, picktype):
|
|
||||||
"""
|
|
||||||
Replace own picks with the one in event
|
|
||||||
:param event: Event that supplies information for comparison
|
|
||||||
:type event: pylot.core.util.event.Event
|
|
||||||
:param picktype: 'auto' or 'manual' picks
|
|
||||||
:type picktype: str
|
|
||||||
:return:
|
|
||||||
:rtype: None
|
|
||||||
"""
|
|
||||||
checkflag = 0
|
|
||||||
picks = event.picks
|
|
||||||
# remove existing picks
|
|
||||||
for j, pick in reversed(list(enumerate(picks))):
|
|
||||||
try:
|
|
||||||
if picktype in str(pick.method_id.id):
|
|
||||||
picks.pop(j)
|
|
||||||
checkflag = 1
|
|
||||||
except AttributeError as e:
|
|
||||||
msg = '{}'.format(e)
|
|
||||||
print(e)
|
|
||||||
checkflag = 0
|
|
||||||
if checkflag:
|
|
||||||
print("Found %s pick(s), remove them and append new picks to catalog." % picktype)
|
|
||||||
|
|
||||||
# append new picks
|
|
||||||
for pick in self.get_evt_data().picks:
|
|
||||||
if picktype in str(pick.method_id.id):
|
|
||||||
picks.append(pick)
|
|
||||||
|
|
||||||
def exportEvent(self, fnout, fnext='.xml', fcheck='auto', upperErrors=None):
|
|
||||||
"""
|
|
||||||
Export event to file
|
|
||||||
:param fnout: basename of file
|
|
||||||
:param fnext: file extensions xml, cnv, obs, focmec, or/and pha
|
|
||||||
:param fcheck: check and delete existing information
|
|
||||||
can be a str or a list of strings of ['manual', 'auto', 'origin', 'magnitude']
|
|
||||||
"""
|
|
||||||
from pylot.core.util.defaults import OUTPUTFORMATS
|
|
||||||
|
|
||||||
if not type(fcheck) == list:
|
|
||||||
fcheck = [fcheck]
|
|
||||||
|
|
||||||
try:
|
|
||||||
evtformat = OUTPUTFORMATS[fnext]
|
|
||||||
except KeyError as e:
|
|
||||||
errmsg = '{0}; selected file extension {1} not ' \
|
|
||||||
'supported'.format(e, fnext)
|
|
||||||
raise FormatError(errmsg)
|
|
||||||
|
|
||||||
if hasattr(self.get_evt_data(), 'notes'):
|
|
||||||
with open(os.path.join(os.path.dirname(fnout), 'notes.txt'), 'w') as notes_file:
|
|
||||||
notes_file.write(self.get_evt_data().notes)
|
|
||||||
|
|
||||||
# check for already existing xml-file
|
|
||||||
if fnext == '.xml':
|
|
||||||
if os.path.isfile(fnout + fnext):
|
|
||||||
print("xml-file already exists! Check content ...")
|
|
||||||
cat = read_events(fnout + fnext)
|
|
||||||
if len(cat) > 1:
|
|
||||||
raise IOError('Ambigious event information in file {}'.format(fnout + fnext))
|
|
||||||
if len(cat) < 1:
|
|
||||||
raise IOError('No event information in file {}'.format(fnout + fnext))
|
|
||||||
event = cat[0]
|
|
||||||
if not event.resource_id == self.get_evt_data().resource_id:
|
|
||||||
QMessageBox.warning(self, 'Warning', 'Different resource IDs!')
|
|
||||||
return
|
|
||||||
self.checkEvent(event, fcheck)
|
|
||||||
self.setEvtData(event)
|
|
||||||
|
|
||||||
self.get_evt_data().write(fnout + fnext, format=evtformat)
|
|
||||||
|
|
||||||
# try exporting event
|
|
||||||
else:
|
|
||||||
evtdata_org = self.get_evt_data()
|
|
||||||
picks = evtdata_org.picks
|
|
||||||
eventpath = evtdata_org.path
|
|
||||||
picks_copy = copy.deepcopy(picks)
|
|
||||||
evtdata_copy = Event(eventpath)
|
|
||||||
evtdata_copy.picks = picks_copy
|
|
||||||
|
|
||||||
# check for stations picked automatically as well as manually
|
|
||||||
# Prefer manual picks!
|
|
||||||
for i in range(len(picks)):
|
|
||||||
if picks[i].method_id == 'manual':
|
|
||||||
mstation = picks[i].waveform_id.station_code
|
|
||||||
mstation_ext = mstation + '_'
|
|
||||||
for k in range(len(picks_copy)):
|
|
||||||
if ((picks_copy[k].waveform_id.station_code == mstation) or
|
|
||||||
(picks_copy[k].waveform_id.station_code == mstation_ext)) and \
|
|
||||||
(picks_copy[k].method_id == 'auto'):
|
|
||||||
del picks_copy[k]
|
|
||||||
break
|
|
||||||
lendiff = len(picks) - len(picks_copy)
|
|
||||||
if lendiff is not 0:
|
|
||||||
print("Manual as well as automatic picks available. Prefered the {} manual ones!".format(lendiff))
|
|
||||||
|
|
||||||
if upperErrors:
|
|
||||||
# check for pick uncertainties exceeding adjusted upper errors
|
|
||||||
# Picks with larger uncertainties will not be saved in output file!
|
|
||||||
for j in range(len(picks)):
|
|
||||||
for i in range(len(picks_copy)):
|
|
||||||
if picks_copy[i].phase_hint[0] == 'P':
|
|
||||||
if (picks_copy[i].time_errors['upper_uncertainty'] >= upperErrors[0]) or \
|
|
||||||
(picks_copy[i].time_errors['uncertainty'] is None):
|
|
||||||
print("Uncertainty exceeds or equal adjusted upper time error!")
|
|
||||||
print("Adjusted uncertainty: {}".format(upperErrors[0]))
|
|
||||||
print("Pick uncertainty: {}".format(picks_copy[i].time_errors['uncertainty']))
|
|
||||||
print("{1} P-Pick of station {0} will not be saved in outputfile".format(
|
|
||||||
picks_copy[i].waveform_id.station_code,
|
|
||||||
picks_copy[i].method_id))
|
|
||||||
print("#")
|
|
||||||
del picks_copy[i]
|
|
||||||
break
|
|
||||||
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):
|
|
||||||
print("Uncertainty exceeds or equal adjusted upper time error!")
|
|
||||||
print("Adjusted uncertainty: {}".format(upperErrors[1]))
|
|
||||||
print("Pick uncertainty: {}".format(picks_copy[i].time_errors['uncertainty']))
|
|
||||||
print("{1} S-Pick of station {0} will not be saved in outputfile".format(
|
|
||||||
picks_copy[i].waveform_id.station_code,
|
|
||||||
picks_copy[i].method_id))
|
|
||||||
print("#")
|
|
||||||
del picks_copy[i]
|
|
||||||
break
|
|
||||||
|
|
||||||
if fnext == '.obs':
|
|
||||||
try:
|
|
||||||
evtdata_copy.write(fnout + fnext, format=evtformat)
|
|
||||||
# write header afterwards
|
|
||||||
evid = str(evtdata_org.resource_id).split('/')[1]
|
|
||||||
header = '# EQEVENT: Label: EQ%s Loc: X 0.00 Y 0.00 Z 10.00 OT 0.00 \n' % evid
|
|
||||||
nllocfile = open(fnout + fnext)
|
|
||||||
l = nllocfile.readlines()
|
|
||||||
nllocfile.close()
|
|
||||||
l.insert(0, header)
|
|
||||||
nllocfile = open(fnout + fnext, 'w')
|
|
||||||
nllocfile.write("".join(l))
|
|
||||||
nllocfile.close()
|
|
||||||
except KeyError as e:
|
|
||||||
raise KeyError('''{0} export format
|
|
||||||
not implemented: {1}'''.format(evtformat, e))
|
|
||||||
if fnext == '.cnv':
|
|
||||||
try:
|
|
||||||
velest.export(picks_copy, fnout + fnext, eventinfo=self.get_evt_data())
|
|
||||||
except KeyError as e:
|
|
||||||
raise KeyError('''{0} export format
|
|
||||||
not implemented: {1}'''.format(evtformat, e))
|
|
||||||
if fnext == '_focmec.in':
|
|
||||||
try:
|
|
||||||
infile = os.path.join(os.path.expanduser('~'), '.pylot', 'pylot.in')
|
|
||||||
print('Using default input file {}'.format(infile))
|
|
||||||
parameter = PylotParameter(infile)
|
|
||||||
focmec.export(picks_copy, fnout + fnext, parameter, eventinfo=self.get_evt_data())
|
|
||||||
except KeyError as e:
|
|
||||||
raise KeyError('''{0} export format
|
|
||||||
not implemented: {1}'''.format(evtformat, e))
|
|
||||||
if fnext == '.pha':
|
|
||||||
try:
|
|
||||||
infile = os.path.join(os.path.expanduser('~'), '.pylot', 'pylot.in')
|
|
||||||
print('Using default input file {}'.format(infile))
|
|
||||||
parameter = PylotParameter(infile)
|
|
||||||
hypodd.export(picks_copy, fnout + fnext, parameter, eventinfo=self.get_evt_data())
|
|
||||||
except KeyError as e:
|
|
||||||
raise KeyError('''{0} export format
|
|
||||||
not implemented: {1}'''.format(evtformat, e))
|
|
||||||
|
|
||||||
def getComp(self):
|
|
||||||
"""
|
|
||||||
Get component (ZNE)
|
|
||||||
"""
|
|
||||||
return self.comp
|
|
||||||
|
|
||||||
def getID(self):
|
|
||||||
"""
|
|
||||||
Get unique resource id
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
return self.evtdata.get('resource_id').id
|
|
||||||
except:
|
|
||||||
return None
|
|
||||||
|
|
||||||
def filterWFData(self, kwargs):
|
|
||||||
"""
|
|
||||||
Filter waveform data
|
|
||||||
:param kwargs: arguments to pass through to filter function
|
|
||||||
"""
|
|
||||||
data = self.getWFData()
|
|
||||||
data.detrend('linear')
|
|
||||||
data.taper(0.02, type='cosine')
|
|
||||||
data.filter(**kwargs)
|
|
||||||
self.dirty = True
|
|
||||||
|
|
||||||
def setWFData(self, fnames, fnames_syn=None, checkRotated=False, metadata=None, tstart=0, tstop=0):
|
|
||||||
"""
|
|
||||||
Clear current waveform data and set given waveform data
|
|
||||||
:param fnames: waveform data names to append
|
|
||||||
:type fnames: list
|
|
||||||
"""
|
|
||||||
self.wfdata = Stream()
|
|
||||||
self.wforiginal = None
|
|
||||||
self.wfsyn = Stream()
|
|
||||||
if tstart == tstop:
|
|
||||||
tstart = tstop = None
|
|
||||||
self.tstart = tstart
|
|
||||||
self.tstop = tstop
|
|
||||||
|
|
||||||
# if obspy_dmt:
|
|
||||||
# wfdir = 'raw'
|
|
||||||
# self.processed = False
|
|
||||||
# for fname in fnames:
|
|
||||||
# if fname.endswith('processed'):
|
|
||||||
# wfdir = 'processed'
|
|
||||||
# self.processed = True
|
|
||||||
# break
|
|
||||||
# for fpath in fnames:
|
|
||||||
# if fpath.endswith(wfdir):
|
|
||||||
# wffnames = [os.path.join(fpath, fname) for fname in os.listdir(fpath)]
|
|
||||||
# if 'syngine' in fpath.split('/')[-1]:
|
|
||||||
# wffnames_syn = [os.path.join(fpath, fname) for fname in os.listdir(fpath)]
|
|
||||||
# else:
|
|
||||||
# wffnames = fnames
|
|
||||||
if fnames is not None:
|
|
||||||
self.appendWFData(fnames)
|
|
||||||
if fnames_syn is not None:
|
|
||||||
self.appendWFData(fnames_syn, synthetic=True)
|
|
||||||
else:
|
|
||||||
return False
|
|
||||||
|
|
||||||
# various pre-processing steps:
|
|
||||||
# remove possible underscores in station names
|
|
||||||
# self.wfdata = remove_underscores(self.wfdata)
|
|
||||||
# check for gaps and merge
|
|
||||||
self.wfdata = check4gapsAndMerge(self.wfdata)
|
|
||||||
# check for stations with rotated components
|
|
||||||
if checkRotated and metadata is not None:
|
|
||||||
self.wfdata = check4rotated(self.wfdata, metadata, verbosity=0)
|
|
||||||
# trim station components to same start value
|
|
||||||
trim_station_components(self.wfdata, trim_start=True, trim_end=False)
|
|
||||||
|
|
||||||
# make a copy of original data
|
|
||||||
self.wforiginal = self.getWFData().copy()
|
|
||||||
self.dirty = False
|
|
||||||
return True
|
|
||||||
|
|
||||||
def appendWFData(self, fnames, synthetic=False):
|
|
||||||
"""
|
|
||||||
Read waveform data from fnames and append it to current wf data
|
|
||||||
:param fnames: waveform data to append
|
|
||||||
:type fnames: list
|
|
||||||
"""
|
|
||||||
assert isinstance(fnames, list), "input parameter 'fnames' is " \
|
|
||||||
"supposed to be of type 'list' " \
|
|
||||||
"but is actually" \
|
|
||||||
" {0}".format(type(fnames))
|
|
||||||
if self.dirty:
|
|
||||||
self.resetWFData()
|
|
||||||
|
|
||||||
real_or_syn_data = {True: self.wfsyn,
|
|
||||||
False: self.wfdata}
|
|
||||||
|
|
||||||
warnmsg = ''
|
|
||||||
for fname in set(fnames):
|
|
||||||
try:
|
|
||||||
real_or_syn_data[synthetic] += read(fname, starttime=self.tstart, endtime=self.tstop)
|
|
||||||
except TypeError:
|
|
||||||
try:
|
|
||||||
real_or_syn_data[synthetic] += read(fname, format='GSE2', starttime=self.tstart, endtime=self.tstop)
|
|
||||||
except Exception as e:
|
|
||||||
try:
|
|
||||||
real_or_syn_data[synthetic] += read(fname, format='SEGY', starttime=self.tstart, endtime=self.tstop)
|
|
||||||
except Exception as e:
|
|
||||||
warnmsg += '{0}\n{1}\n'.format(fname, e)
|
|
||||||
except SacIOError as se:
|
|
||||||
warnmsg += '{0}\n{1}\n'.format(fname, se)
|
|
||||||
if warnmsg:
|
|
||||||
warnmsg = 'WARNING in appendWFData: unable to read waveform data\n' + warnmsg
|
|
||||||
print(warnmsg)
|
|
||||||
|
|
||||||
def getWFData(self):
|
|
||||||
return self.wfdata
|
|
||||||
|
|
||||||
def getOriginalWFData(self):
|
|
||||||
return self.wforiginal
|
|
||||||
|
|
||||||
def getSynWFData(self):
|
|
||||||
return self.wfsyn
|
|
||||||
|
|
||||||
def resetWFData(self):
|
|
||||||
"""
|
|
||||||
Set waveform data to original waveform data
|
|
||||||
"""
|
|
||||||
if self.getOriginalWFData():
|
|
||||||
self.wfdata = self.getOriginalWFData().copy()
|
|
||||||
else:
|
|
||||||
self.wfdata = Stream()
|
|
||||||
self.dirty = False
|
|
||||||
|
|
||||||
def resetPicks(self):
|
|
||||||
"""
|
|
||||||
Clear all picks from event
|
|
||||||
"""
|
|
||||||
self.get_evt_data().picks = []
|
|
||||||
|
|
||||||
def get_evt_data(self):
|
|
||||||
return self.evtdata
|
|
||||||
|
|
||||||
def setEvtData(self, event):
|
|
||||||
self.evtdata = event
|
|
||||||
|
|
||||||
def applyEVTData(self, data, typ='pick', authority_id='rub'):
|
|
||||||
"""
|
|
||||||
Either takes an `obspy.core.event.Event` object and applies all new
|
|
||||||
information on the event to the actual data if typ is 'event or
|
|
||||||
creates ObsPy pick objects and append it to the picks list from the
|
|
||||||
PyLoT dictionary contain all picks if type is pick
|
|
||||||
:param data: data to apply, either picks or complete event
|
|
||||||
:type data:
|
|
||||||
:param typ: which event data to apply, 'pick' or 'event'
|
|
||||||
:type typ: str
|
|
||||||
:param authority_id: (currently unused)
|
|
||||||
:type: str
|
|
||||||
:raise OverwriteError:
|
|
||||||
"""
|
|
||||||
|
|
||||||
def applyPicks(picks):
|
|
||||||
"""
|
|
||||||
Creates ObsPy pick objects and append it to the picks list from the
|
|
||||||
PyLoT dictionary contain all picks.
|
|
||||||
:param picks:
|
|
||||||
:raise OverwriteError: raises an OverwriteError if the picks list is
|
|
||||||
not empty. The GUI will then ask for a decision.
|
|
||||||
"""
|
|
||||||
# firstonset = find_firstonset(picks)
|
|
||||||
# check for automatic picks
|
|
||||||
print("Writing phases to ObsPy-quakeml file")
|
|
||||||
for key in picks:
|
|
||||||
if not picks[key].get('P'):
|
|
||||||
continue
|
|
||||||
if picks[key]['P']['picker'] == 'auto':
|
|
||||||
print("Existing auto-picks will be overwritten in pick-dictionary!")
|
|
||||||
picks = picks_from_picksdict(picks)
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
if self.get_evt_data().picks:
|
|
||||||
raise OverwriteError('Existing picks would be overwritten!')
|
|
||||||
else:
|
|
||||||
picks = picks_from_picksdict(picks)
|
|
||||||
break
|
|
||||||
self.get_evt_data().picks = picks
|
|
||||||
# if 'smi:local' in self.getID() and firstonset:
|
|
||||||
# fonset_str = firstonset.strftime('%Y_%m_%d_%H_%M_%S')
|
|
||||||
# ID = ResourceIdentifier('event/' + fonset_str)
|
|
||||||
# ID.convertIDToQuakeMLURI(authority_id=authority_id)
|
|
||||||
# self.get_evt_data().resource_id = ID
|
|
||||||
|
|
||||||
def applyEvent(event):
|
|
||||||
"""
|
|
||||||
takes an `obspy.core.event.Event` object and applies all new
|
|
||||||
information on the event to the actual data
|
|
||||||
:param event:
|
|
||||||
"""
|
|
||||||
if event is None:
|
|
||||||
print("applyEvent: Received None")
|
|
||||||
return
|
|
||||||
if self.isNew():
|
|
||||||
self.setEvtData(event)
|
|
||||||
else:
|
|
||||||
# prevent overwriting original pick information
|
|
||||||
event_old = self.get_evt_data()
|
|
||||||
if not event_old.resource_id == event.resource_id:
|
|
||||||
print("WARNING: Missmatch in event resource id's: {} and {}".format(
|
|
||||||
event_old.resource_id,
|
|
||||||
event.resource_id))
|
|
||||||
else:
|
|
||||||
picks = copy.deepcopy(event_old.picks)
|
|
||||||
event = merge_picks(event, picks)
|
|
||||||
# apply event information from location
|
|
||||||
event_old.update(event)
|
|
||||||
|
|
||||||
applydata = {'pick': applyPicks,
|
|
||||||
'event': applyEvent}
|
|
||||||
|
|
||||||
applydata[typ](data)
|
|
||||||
self._new = False
|
|
||||||
|
|
||||||
|
|
||||||
class GenericDataStructure(object):
|
class GenericDataStructure(object):
|
||||||
@@ -778,22 +238,6 @@ class PilotDataStructure(GenericDataStructure):
|
|||||||
structure.
|
structure.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, **fields):
|
|
||||||
if not fields:
|
|
||||||
fields = {'database': '2006.01',
|
|
||||||
'root': '/data/Egelados/EVENT_DATA/LOCAL'}
|
|
||||||
|
|
||||||
GenericDataStructure.__init__(self, **fields)
|
|
||||||
|
|
||||||
self.setExpandFields(['root', 'database'])
|
|
||||||
|
|
||||||
|
|
||||||
class ObspyDMTdataStructure(GenericDataStructure):
|
|
||||||
"""
|
|
||||||
Object containing the data access information for the old PILOT data
|
|
||||||
structure.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, **fields):
|
def __init__(self, **fields):
|
||||||
if not fields:
|
if not fields:
|
||||||
fields = {'database': '',
|
fields = {'database': '',
|
||||||
|
|||||||
@@ -511,7 +511,7 @@ defaults = {'rootpath': {'type': str,
|
|||||||
|
|
||||||
'taup_model': {'type': str,
|
'taup_model': {'type': str,
|
||||||
'tooltip': 'Define TauPy model for traveltime estimation. Possible values: 1066a, 1066b, ak135, ak135f, herrin, iasp91, jb, prem, pwdk, sp6',
|
'tooltip': 'Define TauPy model for traveltime estimation. Possible values: 1066a, 1066b, ak135, ak135f, herrin, iasp91, jb, prem, pwdk, sp6',
|
||||||
'value': 'iasp91',
|
'value': None,
|
||||||
'namestring': 'TauPy model'},
|
'namestring': 'TauPy model'},
|
||||||
|
|
||||||
'taup_phases': {'type': str,
|
'taup_phases': {'type': str,
|
||||||
|
|||||||
@@ -0,0 +1,106 @@
|
|||||||
|
import copy
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from typing import Union
|
||||||
|
|
||||||
|
from obspy import read_events
|
||||||
|
from obspy.core.event import Event as ObsPyEvent
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class EventData:
|
||||||
|
evtdata: Union[ObsPyEvent, None] = None
|
||||||
|
_new: bool = False
|
||||||
|
|
||||||
|
def __init__(self, evtdata=None):
|
||||||
|
self.set_event_data(evtdata)
|
||||||
|
|
||||||
|
def set_event_data(self, evtdata):
|
||||||
|
if isinstance(evtdata, ObsPyEvent):
|
||||||
|
self.evtdata = evtdata
|
||||||
|
elif isinstance(evtdata, dict):
|
||||||
|
self.evtdata = self.read_pilot_event(**evtdata)
|
||||||
|
elif isinstance(evtdata, str):
|
||||||
|
self.evtdata = self.read_event_file(evtdata)
|
||||||
|
else:
|
||||||
|
self.set_new()
|
||||||
|
self.evtdata = ObsPyEvent(picks=[])
|
||||||
|
|
||||||
|
def read_event_file(self, evtdata: str) -> ObsPyEvent:
|
||||||
|
try:
|
||||||
|
cat = read_events(evtdata)
|
||||||
|
if len(cat) != 1:
|
||||||
|
raise ValueError(f'ambiguous event information for file: {evtdata}')
|
||||||
|
return cat[0]
|
||||||
|
except TypeError as e:
|
||||||
|
self.handle_event_file_error(e, evtdata)
|
||||||
|
|
||||||
|
def handle_event_file_error(self, e: TypeError, evtdata: str):
|
||||||
|
if 'Unknown format for file' in str(e):
|
||||||
|
if 'PHASES' in evtdata:
|
||||||
|
picks = self.picksdict_from_pilot(evtdata)
|
||||||
|
evtdata = ObsPyEvent(picks=self.picks_from_picksdict(picks))
|
||||||
|
elif 'LOC' in evtdata:
|
||||||
|
raise NotImplementedError('PILOT location information read support not yet implemented.')
|
||||||
|
elif 'event.pkl' in evtdata:
|
||||||
|
evtdata = self.qml_from_obspy_dmt(evtdata)
|
||||||
|
else:
|
||||||
|
raise e
|
||||||
|
else:
|
||||||
|
raise e
|
||||||
|
|
||||||
|
def set_new(self):
|
||||||
|
self._new = True
|
||||||
|
|
||||||
|
def is_new(self) -> bool:
|
||||||
|
return self._new
|
||||||
|
|
||||||
|
def get_picks_str(self) -> str:
|
||||||
|
return '\n'.join(str(pick) for pick in self.evtdata.picks)
|
||||||
|
|
||||||
|
def replace_origin(self, event: ObsPyEvent, force_overwrite: bool = False):
|
||||||
|
if self.evtdata.origins or force_overwrite:
|
||||||
|
event.origins = self.evtdata.origins
|
||||||
|
|
||||||
|
def replace_magnitude(self, event: ObsPyEvent, force_overwrite: bool = False):
|
||||||
|
if self.evtdata.magnitudes or force_overwrite:
|
||||||
|
event.magnitudes = self.evtdata.magnitudes
|
||||||
|
|
||||||
|
def replace_picks(self, event: ObsPyEvent, picktype: str):
|
||||||
|
checkflag = 1
|
||||||
|
picks = event.picks
|
||||||
|
for j, pick in reversed(list(enumerate(picks))):
|
||||||
|
if picktype in str(pick.method_id.id):
|
||||||
|
picks.pop(j)
|
||||||
|
checkflag = 2
|
||||||
|
|
||||||
|
if checkflag > 0:
|
||||||
|
for pick in self.evtdata.picks:
|
||||||
|
if picktype in str(pick.method_id.id):
|
||||||
|
picks.append(pick)
|
||||||
|
|
||||||
|
def get_id(self) -> Union[str, None]:
|
||||||
|
try:
|
||||||
|
return self.evtdata.resource_id.id
|
||||||
|
except:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def apply_event_data(self, data, typ='pick'):
|
||||||
|
if typ == 'pick':
|
||||||
|
self.apply_picks(data)
|
||||||
|
elif typ == 'event':
|
||||||
|
self.apply_event(data)
|
||||||
|
|
||||||
|
def apply_picks(self, picks):
|
||||||
|
self.evtdata.picks = picks
|
||||||
|
|
||||||
|
def apply_event(self, event: ObsPyEvent):
|
||||||
|
if self.is_new():
|
||||||
|
self.evtdata = event
|
||||||
|
else:
|
||||||
|
old_event = self.evtdata
|
||||||
|
if old_event.resource_id == event.resource_id:
|
||||||
|
picks = copy.deepcopy(old_event.picks)
|
||||||
|
event = self.merge_picks(event, picks)
|
||||||
|
old_event.update(event)
|
||||||
|
else:
|
||||||
|
print(f"WARNING: Mismatch in event resource id's: {old_event.resource_id} and {event.resource_id}")
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
"""
|
||||||
|
Script to get event parameters from PyLoT-xml file to write
|
||||||
|
them into eventlist.
|
||||||
|
LK, igem, 03/2021
|
||||||
|
Edited for use in PyLoT
|
||||||
|
JG, igem, 01/2022
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import argparse
|
||||||
|
import numpy as np
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
import glob
|
||||||
|
|
||||||
|
from obspy.core.event import read_events
|
||||||
|
from pyproj import Proj
|
||||||
|
|
||||||
|
"""
|
||||||
|
Creates an eventlist file summarizing all events found in a certain folder. Only called by pressing UI Button eventlis_xml_action
|
||||||
|
|
||||||
|
:rtype:
|
||||||
|
:param path: Path to root folder where single Event folder are to found
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def geteventlistfromxml(path, outpath):
|
||||||
|
p = Proj(proj='utm', zone=32, ellps='WGS84')
|
||||||
|
|
||||||
|
# open eventlist file and write header
|
||||||
|
evlist = outpath + '/eventlist'
|
||||||
|
evlistobj = open(evlist, 'w')
|
||||||
|
evlistobj.write(
|
||||||
|
'EventID Date To Lat Lon EAST NORTH Dep Ml NoP NoS RMS errH errZ Gap \n')
|
||||||
|
|
||||||
|
# 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 location parameters from {}".format(names))
|
||||||
|
cat = read_events(names)
|
||||||
|
try:
|
||||||
|
st = cat.events[0].origins[0].time
|
||||||
|
Lat = cat.events[0].origins[0].latitude
|
||||||
|
Lon = cat.events[0].origins[0].longitude
|
||||||
|
EAST, NORTH = p(Lon, Lat)
|
||||||
|
Dep = cat.events[0].origins[0].depth / 1000
|
||||||
|
Ml = cat.events[0].magnitudes[1].mag
|
||||||
|
NoP = []
|
||||||
|
NoS = []
|
||||||
|
except IndexError:
|
||||||
|
print('Insufficient data found for event (not localised): ' + names.split('/')[-1].split('_')[-1][
|
||||||
|
:-4] + ' Skipping event for eventlist.')
|
||||||
|
continue
|
||||||
|
|
||||||
|
for i in range(len(cat.events[0].origins[0].arrivals)):
|
||||||
|
if cat.events[0].origins[0].arrivals[i].phase == 'P':
|
||||||
|
NoP.append(cat.events[0].origins[0].arrivals[i].phase)
|
||||||
|
elif cat.events[0].origins[0].arrivals[i].phase == 'S':
|
||||||
|
NoS.append(cat.events[0].origins[0].arrivals[i].phase)
|
||||||
|
# NoP = cat.events[0].origins[0].quality.used_station_count
|
||||||
|
errH = cat.events[0].origins[0].origin_uncertainty.max_horizontal_uncertainty
|
||||||
|
errZ = cat.events[0].origins[0].depth_errors.uncertainty
|
||||||
|
Gap = cat.events[0].origins[0].quality.azimuthal_gap
|
||||||
|
# evID = names.split('/')[6]
|
||||||
|
evID = names.split('/')[-1].split('_')[-1][:-4]
|
||||||
|
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) + \
|
||||||
|
'.' + str('%06d' % st.microsecond)
|
||||||
|
|
||||||
|
# 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, \
|
||||||
|
Date, To, Lat, Lon,
|
||||||
|
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)
|
||||||
|
evlistobj.close()
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
from obspy import UTCDateTime
|
from obspy import UTCDateTime
|
||||||
from obspy.core import event as ope
|
from obspy.core import event as ope
|
||||||
|
|
||||||
from pylot.core.util.utils import getLogin, getHash
|
from pylot.core.util.utils import get_login, get_hash
|
||||||
|
|
||||||
|
|
||||||
def create_amplitude(pickID, amp, unit, category, cinfo):
|
def create_amplitude(pickID, amp, unit, category, cinfo):
|
||||||
@@ -61,7 +61,7 @@ def create_creation_info(agency_id=None, creation_time=None, author=None):
|
|||||||
:return:
|
:return:
|
||||||
'''
|
'''
|
||||||
if author is None:
|
if author is None:
|
||||||
author = getLogin()
|
author = get_login()
|
||||||
if creation_time is None:
|
if creation_time is None:
|
||||||
creation_time = UTCDateTime()
|
creation_time = UTCDateTime()
|
||||||
return ope.CreationInfo(agency_id=agency_id, author=author,
|
return ope.CreationInfo(agency_id=agency_id, author=author,
|
||||||
@@ -210,7 +210,7 @@ def create_resourceID(timetohash, restype, authority_id=None, hrstr=None):
|
|||||||
'''
|
'''
|
||||||
assert isinstance(timetohash, UTCDateTime), "'timetohash' is not an ObsPy" \
|
assert isinstance(timetohash, UTCDateTime), "'timetohash' is not an ObsPy" \
|
||||||
"UTCDateTime object"
|
"UTCDateTime object"
|
||||||
hid = getHash(timetohash)
|
hid = get_hash(timetohash)
|
||||||
if hrstr is None:
|
if hrstr is None:
|
||||||
resID = ope.ResourceIdentifier(restype + '/' + hid[0:6])
|
resID = ope.ResourceIdentifier(restype + '/' + hid[0:6])
|
||||||
else:
|
else:
|
||||||
|
|||||||
+316
-767
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,177 @@
|
|||||||
|
import os
|
||||||
|
|
||||||
|
from pylot.core.util.event import Event
|
||||||
|
|
||||||
|
|
||||||
|
class Project(object):
|
||||||
|
'''
|
||||||
|
Pickable class containing information of a PyLoT project, like event lists and file locations.
|
||||||
|
'''
|
||||||
|
|
||||||
|
# TODO: remove rootpath
|
||||||
|
def __init__(self):
|
||||||
|
self.eventlist = []
|
||||||
|
self.location = None
|
||||||
|
self.rootpath = None
|
||||||
|
self.datapath = None
|
||||||
|
self.dirty = False
|
||||||
|
self.parameter = None
|
||||||
|
self._table = None
|
||||||
|
|
||||||
|
def add_eventlist(self, eventlist):
|
||||||
|
'''
|
||||||
|
Add events from an eventlist containing paths to event directories.
|
||||||
|
Will skip existing paths.
|
||||||
|
'''
|
||||||
|
if len(eventlist) == 0:
|
||||||
|
return
|
||||||
|
for item in eventlist:
|
||||||
|
event = Event(item)
|
||||||
|
event.rootpath = self.parameter['rootpath']
|
||||||
|
event.database = self.parameter['database']
|
||||||
|
event.datapath = self.parameter['datapath']
|
||||||
|
if not event.path in self.getPaths():
|
||||||
|
self.eventlist.append(event)
|
||||||
|
self.setDirty()
|
||||||
|
else:
|
||||||
|
print('Skipping event with path {}. Already part of project.'.format(event.path))
|
||||||
|
self.eventlist.sort(key=lambda x: x.pylot_id)
|
||||||
|
self.search_eventfile_info()
|
||||||
|
|
||||||
|
def remove_event(self, event):
|
||||||
|
self.eventlist.remove(event)
|
||||||
|
|
||||||
|
def remove_event_by_id(self, eventID):
|
||||||
|
for event in self.eventlist:
|
||||||
|
if eventID in str(event.resource_id):
|
||||||
|
self.remove_event(event)
|
||||||
|
break
|
||||||
|
|
||||||
|
def read_eventfile_info(self, filename, separator=','):
|
||||||
|
'''
|
||||||
|
Try to read event information from file (:param:filename) comparing specific event datetimes.
|
||||||
|
File structure (each row): event, date, time, magnitude, latitude, longitude, depth
|
||||||
|
separated by :param:separator each.
|
||||||
|
'''
|
||||||
|
with open(filename, 'r') as infile:
|
||||||
|
for line in infile.readlines():
|
||||||
|
eventID, date, time, mag, lat, lon, depth = line.split(separator)[:7]
|
||||||
|
# skip first line
|
||||||
|
try:
|
||||||
|
day, month, year = date.split('/')
|
||||||
|
except:
|
||||||
|
continue
|
||||||
|
year = int(year)
|
||||||
|
# hardcoded, if year only consists of 2 digits (e.g. 16 instead of 2016)
|
||||||
|
if year < 100:
|
||||||
|
year += 2000
|
||||||
|
datetime = '{}-{}-{}T{}'.format(year, month, day, time)
|
||||||
|
try:
|
||||||
|
datetime = UTCDateTime(datetime)
|
||||||
|
except Exception as e:
|
||||||
|
print(e, datetime, filename)
|
||||||
|
continue
|
||||||
|
for event in self.eventlist:
|
||||||
|
if eventID in str(event.resource_id) or eventID in event.origins:
|
||||||
|
if event.origins:
|
||||||
|
origin = event.origins[0] # should have only one origin
|
||||||
|
if origin.time == datetime:
|
||||||
|
origin.latitude = float(lat)
|
||||||
|
origin.longitude = float(lon)
|
||||||
|
origin.depth = float(depth)
|
||||||
|
else:
|
||||||
|
continue
|
||||||
|
elif not event.origins:
|
||||||
|
origin = Origin(resource_id=event.resource_id,
|
||||||
|
time=datetime, latitude=float(lat),
|
||||||
|
longitude=float(lon), depth=float(depth))
|
||||||
|
event.origins.append(origin)
|
||||||
|
event.magnitudes.append(Magnitude(resource_id=event.resource_id,
|
||||||
|
mag=float(mag),
|
||||||
|
mag_type='M'))
|
||||||
|
break
|
||||||
|
|
||||||
|
def search_eventfile_info(self):
|
||||||
|
'''
|
||||||
|
Search all datapaths in rootpath for filenames with given file extension fext
|
||||||
|
and try to read event info from it
|
||||||
|
'''
|
||||||
|
datapaths = []
|
||||||
|
fext = '.csv'
|
||||||
|
for event in self.eventlist:
|
||||||
|
if not event.datapath in datapaths:
|
||||||
|
datapaths.append(event.datapath)
|
||||||
|
for datapath in datapaths:
|
||||||
|
# datapath = os.path.join(self.rootpath, datapath)
|
||||||
|
if os.path.isdir(datapath):
|
||||||
|
for filename in os.listdir(datapath):
|
||||||
|
filename = os.path.join(datapath, filename)
|
||||||
|
if os.path.isfile(filename) and filename.endswith(fext):
|
||||||
|
try:
|
||||||
|
self.read_eventfile_info(filename)
|
||||||
|
except Exception as e:
|
||||||
|
print('Failed on reading eventfile info from file {}: {}'.format(filename, e))
|
||||||
|
else:
|
||||||
|
print("Directory %s does not exist!" % datapath)
|
||||||
|
|
||||||
|
def getPaths(self):
|
||||||
|
'''
|
||||||
|
Returns paths (eventlist) of all events saved in the project.
|
||||||
|
'''
|
||||||
|
paths = []
|
||||||
|
for event in self.eventlist:
|
||||||
|
paths.append(event.path)
|
||||||
|
return paths
|
||||||
|
|
||||||
|
def setDirty(self, value=True):
|
||||||
|
self.dirty = value
|
||||||
|
|
||||||
|
def getEventFromPath(self, path):
|
||||||
|
'''
|
||||||
|
Search for an event in the project by event path.
|
||||||
|
'''
|
||||||
|
for event in self.eventlist:
|
||||||
|
if event.path == path:
|
||||||
|
return event
|
||||||
|
|
||||||
|
def save(self, filename=None):
|
||||||
|
'''
|
||||||
|
Save PyLoT Project to a file.
|
||||||
|
Can be loaded by using project.load(filename).
|
||||||
|
'''
|
||||||
|
try:
|
||||||
|
import pickle
|
||||||
|
except ImportError:
|
||||||
|
import _pickle as pickle
|
||||||
|
|
||||||
|
if filename:
|
||||||
|
self.location = filename
|
||||||
|
else:
|
||||||
|
filename = self.location
|
||||||
|
|
||||||
|
table = self._table # MP: see below
|
||||||
|
try:
|
||||||
|
outfile = open(filename, 'wb')
|
||||||
|
self._table = [] # MP: Workaround as long as table cannot be saved as part of project
|
||||||
|
pickle.dump(self, outfile, protocol=pickle.HIGHEST_PROTOCOL)
|
||||||
|
self.setDirty(False)
|
||||||
|
self._table = table # MP: see above
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
print('Could not pickle PyLoT project. Reason: {}'.format(e))
|
||||||
|
self.setDirty()
|
||||||
|
self._table = table # MP: see above
|
||||||
|
return False
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def load(filename):
|
||||||
|
'''
|
||||||
|
Load project from filename.
|
||||||
|
'''
|
||||||
|
import pickle
|
||||||
|
infile = open(filename, 'rb')
|
||||||
|
project = pickle.load(infile)
|
||||||
|
infile.close()
|
||||||
|
project.location = filename
|
||||||
|
print('Loaded %s' % filename)
|
||||||
|
return project
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import os
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
|
||||||
|
def validate_filenames(filenames: List[str]) -> List[str]:
|
||||||
|
"""
|
||||||
|
validate a list of filenames for file abundance
|
||||||
|
:param filenames: list of possible filenames
|
||||||
|
:type filenames: List[str]
|
||||||
|
:return: list of valid filenames
|
||||||
|
:rtype: List[str]
|
||||||
|
"""
|
||||||
|
return [fn for fn in filenames if os.path.isfile(fn)]
|
||||||
@@ -0,0 +1,123 @@
|
|||||||
|
import logging
|
||||||
|
from dataclasses import dataclass, field
|
||||||
|
from typing import Union, List
|
||||||
|
|
||||||
|
from obspy import Stream, read
|
||||||
|
from obspy.io.sac import SacIOError
|
||||||
|
|
||||||
|
from pylot.core.io.utils import validate_filenames
|
||||||
|
from pylot.core.util.dataprocessing import Metadata
|
||||||
|
from pylot.core.util.utils import get_stations, check_for_nan, check4rotated
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class WaveformData:
|
||||||
|
wfdata: Stream = field(default_factory=Stream)
|
||||||
|
wforiginal: Union[Stream, None] = None
|
||||||
|
wf_alt: Stream = field(default_factory=Stream)
|
||||||
|
dirty: bool = False
|
||||||
|
|
||||||
|
def load_waveforms(self, fnames: List[str], fnames_alt: List[str] = None, check_rotated=False, metadata=None, tstart=0, tstop=0):
|
||||||
|
fn_list = validate_filenames(fnames)
|
||||||
|
if not fn_list:
|
||||||
|
logging.warning('No valid filenames given for loading waveforms')
|
||||||
|
else:
|
||||||
|
self.clear()
|
||||||
|
self.add_waveforms(fn_list)
|
||||||
|
|
||||||
|
if fnames_alt is None:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
alt_fn_list = validate_filenames(fnames_alt)
|
||||||
|
if not alt_fn_list:
|
||||||
|
logging.warning('No valid alternative filenames given for loading waveforms')
|
||||||
|
else:
|
||||||
|
self.add_waveforms(alt_fn_list, alternative=True)
|
||||||
|
|
||||||
|
if not fn_list and not alt_fn_list:
|
||||||
|
logging.error('No filenames or alternative filenames given for loading waveforms')
|
||||||
|
return False
|
||||||
|
|
||||||
|
self.merge()
|
||||||
|
self.replace_nan()
|
||||||
|
if not check_rotated or not metadata:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
self.rotate_zne()
|
||||||
|
self.trim_station_traces()
|
||||||
|
self.wforiginal = self.wfdata.copy()
|
||||||
|
self.dirty = False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def add_waveforms(self, fnames: List[str], alternative: bool = False):
|
||||||
|
data_stream = self.wf_alt if alternative else self.wfdata
|
||||||
|
warnmsg = ''
|
||||||
|
for fname in set(fnames):
|
||||||
|
try:
|
||||||
|
data_stream += read(fname)
|
||||||
|
except TypeError:
|
||||||
|
try:
|
||||||
|
data_stream += read(fname, format='GSE2')
|
||||||
|
except Exception as e:
|
||||||
|
try:
|
||||||
|
data_stream += read(fname, format='SEGY')
|
||||||
|
except Exception as e:
|
||||||
|
warnmsg += f'{fname}\n{e}\n'
|
||||||
|
except SacIOError as se:
|
||||||
|
warnmsg += f'{fname}\n{se}\n'
|
||||||
|
|
||||||
|
if warnmsg:
|
||||||
|
print(f'WARNING in add_waveforms: unable to read waveform data\n{warnmsg}')
|
||||||
|
|
||||||
|
def clear(self):
|
||||||
|
self.wfdata = Stream()
|
||||||
|
self.wforiginal = None
|
||||||
|
self.wf_alt = Stream()
|
||||||
|
|
||||||
|
def reset(self):
|
||||||
|
"""
|
||||||
|
Resets the waveform data to its original state.
|
||||||
|
"""
|
||||||
|
if self.wforiginal:
|
||||||
|
self.wfdata = self.wforiginal.copy()
|
||||||
|
else:
|
||||||
|
self.wfdata = Stream()
|
||||||
|
self.dirty = False
|
||||||
|
|
||||||
|
def merge(self):
|
||||||
|
"""
|
||||||
|
check for gaps in Stream and merge if gaps are found
|
||||||
|
"""
|
||||||
|
gaps = self.wfdata.get_gaps()
|
||||||
|
if gaps:
|
||||||
|
merged = ['{}.{}.{}.{}'.format(*gap[:4]) for gap in gaps]
|
||||||
|
self.wfdata.merge(method=1)
|
||||||
|
logging.info('Merged the following stations because of gaps:')
|
||||||
|
for station in merged:
|
||||||
|
logging.info(station)
|
||||||
|
|
||||||
|
def replace_nan(self):
|
||||||
|
"""
|
||||||
|
Replace all NaNs in data with 0. (in place)
|
||||||
|
"""
|
||||||
|
self.wfdata = check_for_nan(self.wfdata)
|
||||||
|
|
||||||
|
def rotate_zne(self, metadata: Metadata = None):
|
||||||
|
"""
|
||||||
|
Check all traces in stream for rotation. If a trace is not in ZNE rotation (last symbol of channel code is numeric) and the trace
|
||||||
|
is in the metadata with azimuth and dip, rotate it to classical ZNE orientation.
|
||||||
|
Rotating the traces requires them to be of the same length, so, all traces will be trimmed to a common length as a
|
||||||
|
side effect.
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.wfdata = check4rotated(self.wfdata, metadata)
|
||||||
|
|
||||||
|
def trim_station_traces(self):
|
||||||
|
"""
|
||||||
|
trim data stream to common time window
|
||||||
|
"""
|
||||||
|
|
||||||
|
for station in get_stations(self.wfdata):
|
||||||
|
station_traces = self.wfdata.select(station=station)
|
||||||
|
station_traces.trim(starttime=max([trace.stats.starttime for trace in station_traces]),
|
||||||
|
endtime=min([trace.stats.endtime for trace in station_traces]))
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from pylot.core.io.phases import writephases
|
from pylot.core.io.phases import write_phases
|
||||||
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()
|
||||||
@@ -25,4 +25,4 @@ def export(picks, fnout, parameter, eventinfo):
|
|||||||
:type eventinfo: list object
|
:type eventinfo: list object
|
||||||
'''
|
'''
|
||||||
# write phases to FOCMEC-phase file
|
# write phases to FOCMEC-phase file
|
||||||
writephases(picks, 'FOCMEC', fnout, parameter, eventinfo)
|
write_phases(picks, 'FOCMEC', fnout, parameter, eventinfo)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from pylot.core.io.phases import writephases
|
from pylot.core.io.phases import write_phases
|
||||||
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()
|
||||||
@@ -25,4 +25,4 @@ def export(picks, fnout, parameter, eventinfo):
|
|||||||
:type eventinfo: list object
|
:type eventinfo: list object
|
||||||
'''
|
'''
|
||||||
# write phases to HASH-phase file
|
# write phases to HASH-phase file
|
||||||
writephases(picks, 'HASH', fnout, parameter, eventinfo)
|
write_phases(picks, 'HASH', fnout, parameter, eventinfo)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from pylot.core.io.phases import writephases
|
from pylot.core.io.phases import write_phases
|
||||||
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()
|
||||||
@@ -22,4 +22,4 @@ def export(picks, fnout, parameter):
|
|||||||
:type parameter: object
|
:type parameter: object
|
||||||
'''
|
'''
|
||||||
# write phases to HYPO71-phase file
|
# write phases to HYPO71-phase file
|
||||||
writephases(picks, 'HYPO71', fnout, parameter)
|
write_phases(picks, 'HYPO71', fnout, parameter)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from pylot.core.io.phases import writephases
|
from pylot.core.io.phases import write_phases
|
||||||
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()
|
||||||
@@ -25,4 +25,4 @@ def export(picks, fnout, parameter, eventinfo):
|
|||||||
:type eventinfo: list object
|
:type eventinfo: list object
|
||||||
'''
|
'''
|
||||||
# write phases to hypoDD-phase file
|
# write phases to hypoDD-phase file
|
||||||
writephases(picks, 'HYPODD', fnout, parameter, eventinfo)
|
write_phases(picks, 'HYPODD', fnout, parameter, eventinfo)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from pylot.core.io.phases import writephases
|
from pylot.core.io.phases import write_phases
|
||||||
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()
|
||||||
@@ -22,4 +22,4 @@ def export(picks, fnout, parameter):
|
|||||||
:type parameter: object
|
:type parameter: object
|
||||||
'''
|
'''
|
||||||
# write phases to HYPOSAT-phase file
|
# write phases to HYPOSAT-phase file
|
||||||
writephases(picks, 'HYPOSAT', fnout, parameter)
|
write_phases(picks, 'HYPOSAT', fnout, parameter)
|
||||||
|
|||||||
@@ -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 write_phases
|
||||||
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()
|
||||||
@@ -33,7 +34,7 @@ def export(picks, fnout, parameter):
|
|||||||
:type parameter: object
|
:type parameter: object
|
||||||
'''
|
'''
|
||||||
# write phases to NLLoc-phase file
|
# write phases to NLLoc-phase file
|
||||||
writephases(picks, 'NLLoc', fnout, parameter)
|
write_phases(picks, 'NLLoc', fnout, parameter)
|
||||||
|
|
||||||
|
|
||||||
def modify_inputs(ctrfn, root, nllocoutn, phasefn, tttn):
|
def modify_inputs(ctrfn, root, nllocoutn, phasefn, tttn):
|
||||||
@@ -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')
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from pylot.core.io.phases import writephases
|
from pylot.core.io.phases import write_phases
|
||||||
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()
|
||||||
@@ -25,4 +25,4 @@ def export(picks, fnout, eventinfo, parameter=None):
|
|||||||
:type parameter: object
|
:type parameter: object
|
||||||
'''
|
'''
|
||||||
# write phases to VELEST-phase file
|
# write phases to VELEST-phase file
|
||||||
writephases(picks, 'VELEST', fnout, parameter, eventinfo)
|
write_phases(picks, 'VELEST', fnout, parameter, eventinfo)
|
||||||
|
|||||||
+112
-80
@@ -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, PickingFailedException, MissingTraceException
|
||||||
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):
|
||||||
"""
|
"""
|
||||||
@@ -232,20 +232,6 @@ class PickingContainer:
|
|||||||
self.Sflag = 0
|
self.Sflag = 0
|
||||||
|
|
||||||
|
|
||||||
class MissingTraceException(ValueError):
|
|
||||||
"""
|
|
||||||
Used to indicate missing traces in a obspy.core.stream.Stream object
|
|
||||||
"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class PickingFailedException(Exception):
|
|
||||||
"""
|
|
||||||
Raised when picking fails due to missing values etc.
|
|
||||||
"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class AutopickStation(object):
|
class AutopickStation(object):
|
||||||
|
|
||||||
def __init__(self, wfstream, pickparam, verbose, iplot=0, fig_dict=None, metadata=None, origin=None):
|
def __init__(self, wfstream, pickparam, verbose, iplot=0, fig_dict=None, metadata=None, origin=None):
|
||||||
@@ -272,7 +258,7 @@ class AutopickStation(object):
|
|||||||
self.pickparams = copy.deepcopy(pickparam)
|
self.pickparams = copy.deepcopy(pickparam)
|
||||||
self.verbose = verbose
|
self.verbose = verbose
|
||||||
self.iplot = correct_iplot(iplot)
|
self.iplot = correct_iplot(iplot)
|
||||||
self.fig_dict = get_None(fig_dict)
|
self.fig_dict = get_none(fig_dict)
|
||||||
self.metadata = metadata
|
self.metadata = metadata
|
||||||
self.origin = origin
|
self.origin = origin
|
||||||
|
|
||||||
@@ -337,7 +323,8 @@ class AutopickStation(object):
|
|||||||
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 +415,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,17 +457,15 @@ 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."""
|
||||||
if self.pickparams["pstart"] < 0:
|
|
||||||
# TODO here the pickparams is modified, instead of a copy
|
# TODO here the pickparams is modified, instead of a copy
|
||||||
|
if self.pickparams["pstart"] < 0:
|
||||||
self.pickparams["pstart"] = 0
|
self.pickparams["pstart"] = 0
|
||||||
|
if self.pickparams["sstart"] < 0:
|
||||||
|
self.pickparams["sstart"] = 0
|
||||||
|
|
||||||
if self.pickparams["use_taup"] is False or not self.origin or not self.metadata:
|
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
|
||||||
if not self.origin:
|
|
||||||
print('Requested use_taup but no origin given. Exit taupy.')
|
|
||||||
if not self.metadata:
|
|
||||||
print('Requested use_taup but no metadata given. Exit taupy.')
|
|
||||||
exit_taupy()
|
exit_taupy()
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -502,6 +487,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.
|
||||||
@@ -511,6 +510,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:
|
||||||
@@ -518,7 +528,9 @@ 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():
|
||||||
|
if (self.p_results.weight is not None and self.p_results.weight < 4) or \
|
||||||
|
get_bool(self.pickparams.get('use_taup')):
|
||||||
try:
|
try:
|
||||||
self.pick_s_phase()
|
self.pick_s_phase()
|
||||||
except MissingTraceException as mte:
|
except MissingTraceException as mte:
|
||||||
@@ -528,7 +540,7 @@ class AutopickStation(object):
|
|||||||
|
|
||||||
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):
|
||||||
|
|
||||||
@@ -592,12 +604,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
|
||||||
@@ -632,38 +646,44 @@ class AutopickStation(object):
|
|||||||
ax1.set_ylim([-1.5, 1.5])
|
ax1.set_ylim([-1.5, 1.5])
|
||||||
ax1.set_ylabel('Normalized Counts')
|
ax1.set_ylabel('Normalized Counts')
|
||||||
|
|
||||||
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])
|
||||||
@@ -671,15 +691,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')
|
||||||
@@ -720,7 +744,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
|
||||||
@@ -730,14 +755,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
|
||||||
@@ -784,15 +811,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
|
||||||
@@ -823,15 +841,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)
|
||||||
@@ -845,7 +866,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
|
||||||
@@ -853,7 +875,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
|
||||||
@@ -868,22 +891,26 @@ class AutopickStation(object):
|
|||||||
'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)
|
||||||
@@ -891,10 +918,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)
|
||||||
@@ -964,7 +992,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
|
||||||
@@ -1106,7 +1134,9 @@ 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:
|
||||||
|
cuttimesh = (self.pickparams.get('sstart'), self.pickparams.get('sstop'))
|
||||||
|
else:
|
||||||
# determine time window for calculating CF after P onset
|
# determine time window for calculating CF after P onset
|
||||||
cuttimesh = self._calculate_cuttimes(type='S', iteration=1)
|
cuttimesh = self._calculate_cuttimes(type='S', iteration=1)
|
||||||
|
|
||||||
@@ -1119,7 +1149,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
|
||||||
|
|
||||||
@@ -1130,8 +1161,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()
|
||||||
@@ -1155,7 +1188,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.
|
||||||
|
|||||||
@@ -16,11 +16,12 @@ 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
|
||||||
|
|
||||||
|
from pylot.core.pick.utils import PickingFailedException
|
||||||
|
|
||||||
|
|
||||||
class CharacteristicFunction(object):
|
class CharacteristicFunction(object):
|
||||||
"""
|
"""
|
||||||
@@ -150,7 +151,7 @@ class CharacteristicFunction(object):
|
|||||||
if self.cut[0] == 0 and self.cut[1] == 0:
|
if self.cut[0] == 0 and self.cut[1] == 0:
|
||||||
start = 0
|
start = 0
|
||||||
stop = len(self.orig_data[0])
|
stop = len(self.orig_data[0])
|
||||||
elif self.cut[0] == 0 and self.cut[1] is not 0:
|
elif self.cut[0] == 0 and self.cut[1] != 0:
|
||||||
start = 0
|
start = 0
|
||||||
stop = self.cut[1] / self.dt
|
stop = self.cut[1] / self.dt
|
||||||
else:
|
else:
|
||||||
@@ -167,7 +168,7 @@ class CharacteristicFunction(object):
|
|||||||
if self.cut[0] == 0 and self.cut[1] == 0:
|
if self.cut[0] == 0 and self.cut[1] == 0:
|
||||||
start = 0
|
start = 0
|
||||||
stop = min([len(self.orig_data[0]), len(self.orig_data[1])])
|
stop = min([len(self.orig_data[0]), len(self.orig_data[1])])
|
||||||
elif self.cut[0] == 0 and self.cut[1] is not 0:
|
elif self.cut[0] == 0 and self.cut[1] != 0:
|
||||||
start = 0
|
start = 0
|
||||||
stop = min([self.cut[1] / self.dt, len(self.orig_data[0]),
|
stop = min([self.cut[1] / self.dt, len(self.orig_data[0]),
|
||||||
len(self.orig_data[1])])
|
len(self.orig_data[1])])
|
||||||
@@ -187,7 +188,7 @@ class CharacteristicFunction(object):
|
|||||||
start = 0
|
start = 0
|
||||||
stop = min([self.cut[1] / self.dt, len(self.orig_data[0]),
|
stop = min([self.cut[1] / self.dt, len(self.orig_data[0]),
|
||||||
len(self.orig_data[1]), len(self.orig_data[2])])
|
len(self.orig_data[1]), len(self.orig_data[2])])
|
||||||
elif self.cut[0] == 0 and self.cut[1] is not 0:
|
elif self.cut[0] == 0 and self.cut[1] != 0:
|
||||||
start = 0
|
start = 0
|
||||||
stop = self.cut[1] / self.dt
|
stop = self.cut[1] / self.dt
|
||||||
else:
|
else:
|
||||||
@@ -259,7 +260,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 +277,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 and self.getOrder() == 2:
|
||||||
|
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 +326,18 @@ class HOScf(CharacteristicFunction):
|
|||||||
first = ind[0]
|
first = ind[0]
|
||||||
LTA[:first] = LTA[first]
|
LTA[:first] = LTA[first]
|
||||||
|
|
||||||
|
if self.getOrder() > 2:
|
||||||
self.cf = LTA
|
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 +472,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):
|
||||||
"""
|
"""
|
||||||
@@ -465,6 +490,9 @@ class ARHcf(CharacteristicFunction):
|
|||||||
print('Calculating AR-prediction error from both horizontal traces ...')
|
print('Calculating AR-prediction error from both horizontal traces ...')
|
||||||
|
|
||||||
xnp = self.getDataArray(self.getCut())
|
xnp = self.getDataArray(self.getCut())
|
||||||
|
if len(xnp[0]) == 0:
|
||||||
|
raise PickingFailedException('calcCF: Found empty data trace for cut times. Return')
|
||||||
|
|
||||||
n0 = np.isnan(xnp[0].data)
|
n0 = np.isnan(xnp[0].data)
|
||||||
if len(n0) > 1:
|
if len(n0) > 1:
|
||||||
xnp[0].data[n0] = 0
|
xnp[0].data[n0] = 0
|
||||||
@@ -600,7 +628,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):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -401,6 +402,8 @@ class PDFstatistics(object):
|
|||||||
Takes a path as argument.
|
Takes a path as argument.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# TODO: change root to datapath
|
||||||
|
|
||||||
def __init__(self, directory):
|
def __init__(self, directory):
|
||||||
"""Initiates some values needed when dealing with pdfs later"""
|
"""Initiates some values needed when dealing with pdfs later"""
|
||||||
self._rootdir = directory
|
self._rootdir = directory
|
||||||
|
|||||||
+12
-10
@@ -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
|
||||||
@@ -177,7 +178,9 @@ class AICPicker(AutoPicker):
|
|||||||
aic = tap * self.cf + max(abs(self.cf))
|
aic = tap * self.cf + max(abs(self.cf))
|
||||||
# smooth AIC-CF
|
# smooth AIC-CF
|
||||||
ismooth = int(round(self.Tsmooth / self.dt))
|
ismooth = int(round(self.Tsmooth / self.dt))
|
||||||
aicsmooth = np.zeros(len(aic))
|
# MP MP better start with original data than zeros if array shall be smoothed, created artificial value before
|
||||||
|
# when starting with i in range(1...) loop below and subtracting offset afterwards
|
||||||
|
aicsmooth = np.copy(aic)
|
||||||
if len(aic) < ismooth:
|
if len(aic) < ismooth:
|
||||||
print('AICPicker: Tsmooth larger than CF!')
|
print('AICPicker: Tsmooth larger than CF!')
|
||||||
return
|
return
|
||||||
@@ -187,7 +190,7 @@ class AICPicker(AutoPicker):
|
|||||||
ii1 = i - ismooth
|
ii1 = i - ismooth
|
||||||
aicsmooth[i] = aicsmooth[i - 1] + (aic[i] - aic[ii1]) / ismooth
|
aicsmooth[i] = aicsmooth[i - 1] + (aic[i] - aic[ii1]) / ismooth
|
||||||
else:
|
else:
|
||||||
aicsmooth[i] = np.mean(aic[1: i])
|
aicsmooth[i] = np.mean(aic[0: i]) # MP MP created np.nan for i=1
|
||||||
# remove offset in AIC function
|
# remove offset in AIC function
|
||||||
offset = abs(min(aic) - min(aicsmooth))
|
offset = abs(min(aic) - min(aicsmooth))
|
||||||
aicsmooth = aicsmooth - offset
|
aicsmooth = aicsmooth - offset
|
||||||
@@ -196,7 +199,7 @@ class AICPicker(AutoPicker):
|
|||||||
# minimum in AIC function
|
# minimum in AIC function
|
||||||
icfmax = np.argmax(cf)
|
icfmax = np.argmax(cf)
|
||||||
|
|
||||||
# MP MP testing threshold
|
# TODO: If this shall be kept, maybe add thresh_factor to pylot parameters
|
||||||
thresh_hit = False
|
thresh_hit = False
|
||||||
thresh_factor = 0.7
|
thresh_factor = 0.7
|
||||||
thresh = thresh_factor * cf[icfmax]
|
thresh = thresh_factor * cf[icfmax]
|
||||||
@@ -208,7 +211,6 @@ class AICPicker(AutoPicker):
|
|||||||
if sample <= cf[index - 1]:
|
if sample <= cf[index - 1]:
|
||||||
icfmax = index - 1
|
icfmax = index - 1
|
||||||
break
|
break
|
||||||
# MP MP ---
|
|
||||||
|
|
||||||
# find minimum in AIC-CF front of maximum of HOS/AR-CF
|
# find minimum in AIC-CF front of maximum of HOS/AR-CF
|
||||||
lpickwindow = int(round(self.PickWindow / self.dt))
|
lpickwindow = int(round(self.PickWindow / self.dt))
|
||||||
@@ -335,7 +337,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 +478,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 +510,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
|
||||||
|
|||||||
+45
-30
@@ -7,13 +7,15 @@
|
|||||||
|
|
||||||
:author: Ludger Kueperkoch, BESTEC GmbH
|
:author: Ludger Kueperkoch, BESTEC GmbH
|
||||||
"""
|
"""
|
||||||
|
|
||||||
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'):
|
||||||
@@ -60,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
|
||||||
@@ -134,7 +136,7 @@ def earllatepicker(X, nfac, TSNR, Pick1, iplot=0, verbosity=1, fig=None, linecol
|
|||||||
PickError = symmetrize_error(diffti_te, diffti_tl)
|
PickError = symmetrize_error(diffti_te, diffti_tl)
|
||||||
|
|
||||||
if iplot > 1:
|
if iplot > 1:
|
||||||
if get_None(fig) is None:
|
if get_none(fig) is None:
|
||||||
fig = plt.figure() # iplot)
|
fig = plt.figure() # iplot)
|
||||||
plt_flag = 1
|
plt_flag = 1
|
||||||
fig._tight = True
|
fig._tight = True
|
||||||
@@ -342,7 +344,7 @@ def fmpicker(Xraw, Xfilt, pickwin, Pick, iplot=0, fig=None, linecolor='k'):
|
|||||||
print("fmpicker: Found polarity %s" % FM)
|
print("fmpicker: Found polarity %s" % FM)
|
||||||
|
|
||||||
if iplot > 1:
|
if iplot > 1:
|
||||||
if get_None(fig) is None:
|
if get_none(fig) is None:
|
||||||
fig = plt.figure() # iplot)
|
fig = plt.figure() # iplot)
|
||||||
plt_flag = 1
|
plt_flag = 1
|
||||||
fig._tight = True
|
fig._tight = True
|
||||||
@@ -535,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
|
||||||
@@ -595,6 +598,8 @@ def select_for_phase(st, phase):
|
|||||||
alter_comp = compclass.getCompPosition(comp)
|
alter_comp = compclass.getCompPosition(comp)
|
||||||
alter_comp = str(alter_comp[0])
|
alter_comp = str(alter_comp[0])
|
||||||
sel_st += st.select(component=comp)
|
sel_st += st.select(component=comp)
|
||||||
|
if len(sel_st) < 1:
|
||||||
|
sel_st += st.select(component="Q")
|
||||||
sel_st += st.select(component=alter_comp)
|
sel_st += st.select(component=alter_comp)
|
||||||
elif phase.upper() == 'S':
|
elif phase.upper() == 'S':
|
||||||
comps = 'NE'
|
comps = 'NE'
|
||||||
@@ -811,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
|
||||||
@@ -863,7 +868,7 @@ def checksignallength(X, pick, minsiglength, pickparams, iplot=0, fig=None, line
|
|||||||
returnflag = 0
|
returnflag = 0
|
||||||
|
|
||||||
if iplot > 1:
|
if iplot > 1:
|
||||||
if get_None(fig) is None:
|
if get_none(fig) is None:
|
||||||
fig = plt.figure() # iplot)
|
fig = plt.figure() # iplot)
|
||||||
plt_flag = 1
|
plt_flag = 1
|
||||||
fig._tight = True
|
fig._tight = True
|
||||||
@@ -885,6 +890,8 @@ def checksignallength(X, pick, minsiglength, pickparams, iplot=0, fig=None, line
|
|||||||
input()
|
input()
|
||||||
except SyntaxError:
|
except SyntaxError:
|
||||||
pass
|
pass
|
||||||
|
except EOFError:
|
||||||
|
pass
|
||||||
plt.close(fig)
|
plt.close(fig)
|
||||||
|
|
||||||
return returnflag
|
return returnflag
|
||||||
@@ -1125,7 +1132,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
|
||||||
@@ -1206,14 +1213,14 @@ def checkZ4S(X, pick, pickparams, iplot, fig=None, linecolor='k'):
|
|||||||
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
|
||||||
plt_flag = 1
|
plt_flag = 1
|
||||||
ax1 = fig.add_subplot(3, 1, i + 1)
|
ax1 = fig.add_subplot(3, 1, i + 1)
|
||||||
ax = ax1
|
ax = ax1
|
||||||
ax.set_title('CheckZ4S, Station %s' % zdat[0].stats.station)
|
ax.set_title('CheckZ4S, Station %s' % zdat[0].stats.station)
|
||||||
else:
|
else:
|
||||||
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
|
||||||
plt_flag = 1
|
plt_flag = 1
|
||||||
ax = fig.add_subplot(3, 1, i + 1, sharex=ax1)
|
ax = fig.add_subplot(3, 1, i + 1, sharex=ax1)
|
||||||
@@ -1315,6 +1322,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 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
|
||||||
@@ -1325,20 +1333,6 @@ 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):
|
|
||||||
"""
|
|
||||||
Replace all NaNs in data with nan_value
|
|
||||||
:param data: array holding data
|
|
||||||
:type data: `~numpy.ndarray`
|
|
||||||
:param nan_value: value which all NaNs are set to
|
|
||||||
:type nan_value: float, int
|
|
||||||
:return: data array with all NaNs replaced with nan_value
|
|
||||||
:rtype: `~numpy.ndarray`
|
|
||||||
"""
|
|
||||||
nn = np.isnan(data)
|
|
||||||
if np.any(nn):
|
|
||||||
data[nn] = nan_value
|
|
||||||
return data
|
|
||||||
|
|
||||||
def taper_cf(cf):
|
def taper_cf(cf):
|
||||||
"""
|
"""
|
||||||
@@ -1351,6 +1345,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
|
||||||
@@ -1361,6 +1356,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
|
||||||
@@ -1389,6 +1385,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
|
||||||
@@ -1471,8 +1468,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
|
||||||
@@ -1486,15 +1485,16 @@ 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
|
||||||
|
|
||||||
if get_None(uncertainty) is None:
|
if get_none(uncertainty) is None:
|
||||||
return quality
|
return quality
|
||||||
|
|
||||||
if uncertainty <= Errors[0]:
|
if uncertainty <= Errors[0]:
|
||||||
@@ -1513,7 +1513,22 @@ def getQualityFromUncertainty(uncertainty, Errors):
|
|||||||
|
|
||||||
return quality
|
return quality
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
import doctest
|
import doctest
|
||||||
|
|
||||||
doctest.testmod()
|
doctest.testmod()
|
||||||
|
|
||||||
|
|
||||||
|
class PickingFailedException(Exception):
|
||||||
|
"""
|
||||||
|
Raised when picking fails due to missing values etc.
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class MissingTraceException(ValueError):
|
||||||
|
"""
|
||||||
|
Used to indicate missing traces in a obspy.core.stream.Stream object
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|||||||
+354
-294
@@ -1,38 +1,52 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
import os
|
import traceback
|
||||||
|
|
||||||
|
import cartopy.crs as ccrs
|
||||||
|
import cartopy.feature as cf
|
||||||
|
import matplotlib
|
||||||
|
import matplotlib.patheffects as PathEffects
|
||||||
import matplotlib.pyplot as plt
|
import matplotlib.pyplot as plt
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import obspy
|
import obspy
|
||||||
import traceback
|
from PySide2 import QtWidgets
|
||||||
from PySide import QtGui
|
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
|
||||||
from matplotlib.figure import Figure
|
|
||||||
from mpl_toolkits.axes_grid1.inset_locator import inset_axes
|
from mpl_toolkits.axes_grid1.inset_locator import inset_axes
|
||||||
from mpl_toolkits.basemap import Basemap
|
from pylot.core.util.utils import identifyPhaseID
|
||||||
from scipy.interpolate import griddata
|
from scipy.interpolate import griddata
|
||||||
|
|
||||||
from pylot.core.util.widgets import PickDlg, PylotCanvas
|
|
||||||
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
|
||||||
|
|
||||||
plt.interactive(False)
|
matplotlib.use('Qt5Agg')
|
||||||
|
|
||||||
|
|
||||||
class Array_map(QtGui.QWidget):
|
class MplCanvas(FigureCanvas):
|
||||||
def __init__(self, parent, metadata, parameter=None, figure=None, annotate=True, pointsize=25.,
|
|
||||||
|
def __init__(self, parent=None, extern_axes=None, width=5, height=4, dpi=100):
|
||||||
|
if extern_axes is None:
|
||||||
|
self.fig = plt.figure(figsize=(width, height), dpi=dpi)
|
||||||
|
self.axes = self.fig.add_subplot(111)
|
||||||
|
else:
|
||||||
|
self.fig = extern_axes.figure
|
||||||
|
self.axes = extern_axes
|
||||||
|
|
||||||
|
super(MplCanvas, self).__init__(self.fig)
|
||||||
|
|
||||||
|
|
||||||
|
class Array_map(QtWidgets.QWidget):
|
||||||
|
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, parent=parent)
|
||||||
Create a map of the array.
|
|
||||||
:param parent: object of PyLoT Mainwindow class
|
assert (parameter is not None or parent is not None), 'either parent or parameter has to be set'
|
||||||
:param parameter: object of PyLoT parameter class
|
# set properties
|
||||||
:param figure:
|
|
||||||
'''
|
|
||||||
QtGui.QWidget.__init__(self)
|
|
||||||
assert (parameter != None or parent != None), 'either parent or parameter has to be set'
|
|
||||||
self._parent = parent
|
self._parent = parent
|
||||||
self.metadata = metadata
|
self.metadata = metadata
|
||||||
self.pointsize = pointsize
|
self.pointsize = pointsize
|
||||||
self.linewidth = linewidth
|
self.linewidth = linewidth
|
||||||
|
self.extern_plot_axes = axes
|
||||||
self.width = width
|
self.width = width
|
||||||
self.height = height
|
self.height = height
|
||||||
self.annotate = annotate
|
self.annotate = annotate
|
||||||
@@ -43,39 +57,235 @@ class Array_map(QtGui.QWidget):
|
|||||||
self.hybrids_dict = None
|
self.hybrids_dict = None
|
||||||
self.eventLoc = None
|
self.eventLoc = None
|
||||||
self.parameter = parameter if parameter else parent._inputs
|
self.parameter = parameter if parameter else parent._inputs
|
||||||
self.figure = figure
|
|
||||||
self.picks_rel = {}
|
self.picks_rel = {}
|
||||||
self.marked_stations = []
|
self.marked_stations = []
|
||||||
self.highlighted_stations = []
|
self.highlighted_stations = []
|
||||||
|
|
||||||
|
# call functions to draw everything
|
||||||
self.init_graphics()
|
self.init_graphics()
|
||||||
self.init_stations()
|
self.init_stations()
|
||||||
self.init_basemap(resolution='l')
|
self.init_crtpyMap()
|
||||||
self.init_map()
|
self.init_map()
|
||||||
|
# set original map limits to fall back on when home button is pressed
|
||||||
|
self.org_xlim = self.canvas.axes.get_xlim()
|
||||||
|
self.org_ylim = self.canvas.axes.get_ylim()
|
||||||
|
# initial map without event
|
||||||
|
self.canvas.axes.set_xlim(self.org_xlim[0], self.org_xlim[1])
|
||||||
|
self.canvas.axes.set_ylim(self.org_ylim[0], self.org_ylim[1])
|
||||||
|
|
||||||
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 update_hybrids_dict(self):
|
|
||||||
self.hybrids_dict = self.picks_dict.copy()
|
|
||||||
for station, pick in self.autopicks_dict.items():
|
|
||||||
if not station in self.hybrids_dict.keys():
|
|
||||||
self.hybrids_dict[station] = pick
|
|
||||||
return self.hybrids_dict
|
|
||||||
|
|
||||||
def init_map(self):
|
def init_map(self):
|
||||||
self.init_colormap()
|
self.init_colormap()
|
||||||
self.connectSignals()
|
self.connectSignals()
|
||||||
self.draw_everything()
|
self.draw_everything()
|
||||||
self.canvas.setZoomBorders2content()
|
|
||||||
|
def init_graphics(self):
|
||||||
|
"""
|
||||||
|
Initializes all GUI components and figure elements to be populated by other functions
|
||||||
|
"""
|
||||||
|
# initialize figure elements
|
||||||
|
|
||||||
|
if self.extern_plot_axes is None:
|
||||||
|
self.canvas = MplCanvas(self)
|
||||||
|
self.plotWidget = FigureCanvas(self.canvas.fig)
|
||||||
|
else:
|
||||||
|
self.canvas = MplCanvas(self, extern_axes=self.extern_plot_axes)
|
||||||
|
self.plotWidget = FigureCanvas(self.canvas.fig)
|
||||||
|
|
||||||
|
# initialize GUI elements
|
||||||
|
self.status_label = QtWidgets.QLabel()
|
||||||
|
self.map_reset_button = QtWidgets.QPushButton('Reset Map View')
|
||||||
|
self.save_map_button = QtWidgets.QPushButton('Save Map')
|
||||||
|
self.go2eq_button = QtWidgets.QPushButton('Go to Event Location')
|
||||||
|
|
||||||
|
self.main_box = QtWidgets.QVBoxLayout()
|
||||||
|
self.setLayout(self.main_box)
|
||||||
|
|
||||||
|
self.top_row = QtWidgets.QHBoxLayout()
|
||||||
|
self.main_box.addLayout(self.top_row, 1)
|
||||||
|
|
||||||
|
self.comboBox_phase = QtWidgets.QComboBox()
|
||||||
|
self.comboBox_phase.insertItem(0, 'P')
|
||||||
|
self.comboBox_phase.insertItem(1, 'S')
|
||||||
|
|
||||||
|
self.comboBox_am = QtWidgets.QComboBox()
|
||||||
|
self.comboBox_am.insertItem(0, 'hybrid (prefer manual)')
|
||||||
|
self.comboBox_am.insertItem(1, 'manual')
|
||||||
|
self.comboBox_am.insertItem(2, 'auto')
|
||||||
|
|
||||||
|
self.annotations_box = QtWidgets.QCheckBox('Annotate')
|
||||||
|
self.annotations_box.setChecked(True)
|
||||||
|
self.auto_refresh_box = QtWidgets.QCheckBox('Automatic refresh')
|
||||||
|
self.auto_refresh_box.setChecked(True)
|
||||||
|
self.refresh_button = QtWidgets.QPushButton('Refresh')
|
||||||
|
self.cmaps_box = QtWidgets.QComboBox()
|
||||||
|
self.cmaps_box.setMaxVisibleItems(20)
|
||||||
|
[self.cmaps_box.addItem(map_name) for map_name in sorted(plt.colormaps())]
|
||||||
|
# try to set to viridis as default
|
||||||
|
self.cmaps_box.setCurrentIndex(self.cmaps_box.findText('viridis'))
|
||||||
|
|
||||||
|
self.top_row.addWidget(QtWidgets.QLabel('Select a phase: '))
|
||||||
|
self.top_row.addWidget(self.comboBox_phase)
|
||||||
|
self.top_row.setStretch(1, 1) # set stretch of item 1 to 1
|
||||||
|
self.top_row.addWidget(QtWidgets.QLabel('Pick type: '))
|
||||||
|
self.top_row.addWidget(self.comboBox_am)
|
||||||
|
self.top_row.setStretch(3, 1) # set stretch of item 1 to 1
|
||||||
|
self.top_row.addWidget(self.cmaps_box)
|
||||||
|
self.top_row.addWidget(self.annotations_box)
|
||||||
|
self.top_row.addWidget(self.auto_refresh_box)
|
||||||
|
self.top_row.addWidget(self.refresh_button)
|
||||||
|
|
||||||
|
self.main_box.addWidget(self.plotWidget, 1)
|
||||||
|
|
||||||
|
self.bot_row = QtWidgets.QHBoxLayout()
|
||||||
|
self.main_box.addLayout(self.bot_row, 0.3)
|
||||||
|
self.bot_row.addWidget(QtWidgets.QLabel(''), 5)
|
||||||
|
self.bot_row.addWidget(self.map_reset_button, 2)
|
||||||
|
self.bot_row.addWidget(self.go2eq_button, 2)
|
||||||
|
self.bot_row.addWidget(self.save_map_button, 2)
|
||||||
|
self.bot_row.addWidget(self.status_label, 5)
|
||||||
|
|
||||||
def init_colormap(self):
|
def init_colormap(self):
|
||||||
self.init_lat_lon_dimensions()
|
self.init_lat_lon_dimensions()
|
||||||
self.init_lat_lon_grid()
|
self.init_lat_lon_grid()
|
||||||
self.init_x_y_dimensions()
|
|
||||||
|
def init_crtpyMap(self):
|
||||||
|
self.canvas.axes.cla()
|
||||||
|
self.canvas.axes = plt.axes(projection=ccrs.PlateCarree())
|
||||||
|
self.canvas.axes.add_feature(cf.LAND)
|
||||||
|
self.canvas.axes.add_feature(cf.OCEAN)
|
||||||
|
self.canvas.axes.add_feature(cf.COASTLINE, linewidth=1, edgecolor='gray')
|
||||||
|
self.canvas.axes.add_feature(cf.BORDERS, alpha=0.7)
|
||||||
|
self.canvas.axes.add_feature(cf.LAKES, alpha=0.7)
|
||||||
|
self.canvas.axes.add_feature(cf.RIVERS, linewidth=1)
|
||||||
|
|
||||||
|
# parallels and meridians
|
||||||
|
self.add_merid_paral()
|
||||||
|
|
||||||
|
self.canvas.fig.tight_layout()
|
||||||
|
|
||||||
|
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)
|
||||||
|
# TODO: current cartopy version does not support label removal. Devs are working on it.
|
||||||
|
# Should be fixed in coming cartopy versions
|
||||||
|
# self.gridlines.xformatter = LONGITUDE_FORMATTER
|
||||||
|
# self.gridlines.yformatter = LATITUDE_FORMATTER
|
||||||
|
|
||||||
|
def remove_merid_paral(self):
|
||||||
|
if len(self.gridlines.xline_artists):
|
||||||
|
self.gridlines.xline_artists[0].remove()
|
||||||
|
self.gridlines.yline_artists[0].remove()
|
||||||
|
|
||||||
|
def org_map_view(self):
|
||||||
|
self.canvas.axes.set_xlim(self.org_xlim[0], self.org_xlim[1])
|
||||||
|
self.canvas.axes.set_ylim(self.org_ylim[0], self.org_ylim[1])
|
||||||
|
# parallels and meridians
|
||||||
|
self.remove_merid_paral()
|
||||||
|
self.add_merid_paral()
|
||||||
|
|
||||||
|
self.canvas.axes.figure.canvas.draw_idle()
|
||||||
|
|
||||||
|
def go2eq(self):
|
||||||
|
if self.eventLoc:
|
||||||
|
lats, lons = self.eventLoc
|
||||||
|
self.canvas.axes.set_xlim(lons - 10, lons + 10)
|
||||||
|
self.canvas.axes.set_ylim(lats - 5, lats + 5)
|
||||||
|
# parallels and meridians
|
||||||
|
self.remove_merid_paral()
|
||||||
|
self.add_merid_paral()
|
||||||
|
|
||||||
|
self.canvas.axes.figure.canvas.draw_idle()
|
||||||
|
|
||||||
|
else:
|
||||||
|
self.status_label.setText('No event information available')
|
||||||
|
|
||||||
|
def connectSignals(self):
|
||||||
|
self.comboBox_phase.currentIndexChanged.connect(self._refresh_drawings)
|
||||||
|
self.comboBox_am.currentIndexChanged.connect(self._refresh_drawings)
|
||||||
|
self.cmaps_box.currentIndexChanged.connect(self._refresh_drawings)
|
||||||
|
self.annotations_box.stateChanged.connect(self.switch_annotations)
|
||||||
|
self.refresh_button.clicked.connect(self._refresh_drawings)
|
||||||
|
self.map_reset_button.clicked.connect(self.org_map_view)
|
||||||
|
self.go2eq_button.clicked.connect(self.go2eq)
|
||||||
|
self.save_map_button.clicked.connect(self.saveFigure)
|
||||||
|
|
||||||
|
self.plotWidget.mpl_connect('motion_notify_event', self.mouse_moved)
|
||||||
|
self.plotWidget.mpl_connect('scroll_event', self.mouse_scroll)
|
||||||
|
self.plotWidget.mpl_connect('button_press_event', self.mouseLeftPress)
|
||||||
|
self.plotWidget.mpl_connect('button_release_event', self.mouseLeftRelease)
|
||||||
|
|
||||||
|
# set mouse events -----------------------------------------------------
|
||||||
|
def mouse_moved(self, event):
|
||||||
|
if not event.inaxes == self.canvas.axes:
|
||||||
|
return
|
||||||
|
lat = event.ydata
|
||||||
|
lon = event.xdata
|
||||||
|
self.status_label.setText('Latitude: {:3.5f}, Longitude: {:3.5f}'.format(lat, lon))
|
||||||
|
|
||||||
|
def mouse_scroll(self, event):
|
||||||
|
if not event.inaxes == self.canvas.axes:
|
||||||
|
return
|
||||||
|
|
||||||
|
zoom = {'up': 1. / 2., 'down': 2.}
|
||||||
|
|
||||||
|
if event.button in zoom:
|
||||||
|
xlim = self.canvas.axes.get_xlim()
|
||||||
|
ylim = self.canvas.axes.get_ylim()
|
||||||
|
|
||||||
|
x, y = event.xdata, event.ydata
|
||||||
|
|
||||||
|
factor = zoom[event.button]
|
||||||
|
xdiff = (xlim[1] - xlim[0]) * factor
|
||||||
|
xl = x - 0.5 * xdiff
|
||||||
|
xr = x + 0.5 * xdiff
|
||||||
|
ydiff = (ylim[1] - ylim[0]) * factor
|
||||||
|
yb = y - 0.5 * ydiff
|
||||||
|
yt = y + 0.5 * ydiff
|
||||||
|
|
||||||
|
self.canvas.axes.set_xlim(xl, xr)
|
||||||
|
self.canvas.axes.set_ylim(yb, yt)
|
||||||
|
# parallels and meridians
|
||||||
|
self.remove_merid_paral()
|
||||||
|
self.add_merid_paral()
|
||||||
|
|
||||||
|
self.canvas.axes.figure.canvas.draw_idle()
|
||||||
|
|
||||||
|
def mouseLeftPress(self, event):
|
||||||
|
if not event.inaxes == self.canvas.axes:
|
||||||
|
return
|
||||||
|
self.map_x = event.xdata
|
||||||
|
self.map_y = event.ydata
|
||||||
|
self.map_xlim = self.canvas.axes.get_xlim()
|
||||||
|
self.map_ylim = self.canvas.axes.get_ylim()
|
||||||
|
|
||||||
|
def mouseLeftRelease(self, event):
|
||||||
|
if not event.inaxes == self.canvas.axes:
|
||||||
|
return
|
||||||
|
new_x = event.xdata
|
||||||
|
new_y = event.ydata
|
||||||
|
|
||||||
|
dx = new_x - self.map_x
|
||||||
|
dy = new_y - self.map_y
|
||||||
|
|
||||||
|
self.canvas.axes.set_xlim((self.map_xlim[0] - dx, self.map_xlim[1] - dx))
|
||||||
|
self.canvas.axes.set_ylim(self.map_ylim[0] - dy, self.map_ylim[1] - dy)
|
||||||
|
# parallels and meridians
|
||||||
|
self.remove_merid_paral()
|
||||||
|
self.add_merid_paral()
|
||||||
|
|
||||||
|
self.canvas.axes.figure.canvas.draw_idle()
|
||||||
|
|
||||||
def onpick(self, event):
|
def onpick(self, event):
|
||||||
|
btn_msg = {1: ' in selection. Aborted', 2: ' to delete a pick on. Aborted', 3: ' to display info.'}
|
||||||
ind = event.ind
|
ind = event.ind
|
||||||
button = event.mouseevent.button
|
button = event.mouseevent.button
|
||||||
if ind == []:
|
msg_reason = None
|
||||||
|
if len(ind) > 1:
|
||||||
|
self._parent.update_status(f'Found more than one station {btn_msg.get(button)}')
|
||||||
return
|
return
|
||||||
if button == 1:
|
if button == 1:
|
||||||
self.openPickDlg(ind)
|
self.openPickDlg(ind)
|
||||||
@@ -84,6 +294,14 @@ class Array_map(QtGui.QWidget):
|
|||||||
elif button == 3:
|
elif button == 3:
|
||||||
self.pickInfo(ind)
|
self.pickInfo(ind)
|
||||||
|
|
||||||
|
# data handling -----------------------------------------------------
|
||||||
|
def update_hybrids_dict(self):
|
||||||
|
self.hybrids_dict = self.picks_dict.copy()
|
||||||
|
for station, pick in self.autopicks_dict.items():
|
||||||
|
if not station in self.hybrids_dict.keys():
|
||||||
|
self.hybrids_dict[station] = pick
|
||||||
|
return self.hybrids_dict
|
||||||
|
|
||||||
def deletePick(self, ind):
|
def deletePick(self, ind):
|
||||||
self.update_hybrids_dict()
|
self.update_hybrids_dict()
|
||||||
for index in ind:
|
for index in ind:
|
||||||
@@ -117,15 +335,6 @@ class Array_map(QtGui.QWidget):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
print('Could not delete pick for station {}.{}: {}'.format(network, station, e))
|
print('Could not delete pick for station {}.{}: {}'.format(network, station, e))
|
||||||
|
|
||||||
def highlight_station(self, network, station, color):
|
|
||||||
stat_dict = self.stations_dict['{}.{}'.format(network, station)]
|
|
||||||
lat = stat_dict['latitude']
|
|
||||||
lon = stat_dict['longitude']
|
|
||||||
self.highlighted_stations.append(self.basemap.scatter(lon, lat, s=self.pointsize, edgecolors=color,
|
|
||||||
facecolors='none', latlon=True,
|
|
||||||
zorder=12, label='deleted'))
|
|
||||||
self.canvas.draw()
|
|
||||||
|
|
||||||
def pickInfo(self, ind):
|
def pickInfo(self, ind):
|
||||||
self.update_hybrids_dict()
|
self.update_hybrids_dict()
|
||||||
for index in ind:
|
for index in ind:
|
||||||
@@ -139,57 +348,6 @@ class Array_map(QtGui.QWidget):
|
|||||||
for key, info in picks.items():
|
for key, info in picks.items():
|
||||||
print('{}: {}'.format(key, info))
|
print('{}: {}'.format(key, info))
|
||||||
|
|
||||||
def openPickDlg(self, ind):
|
|
||||||
data = self._parent.get_data().getWFData()
|
|
||||||
for index in ind:
|
|
||||||
network, station = self._station_onpick_ids[index].split('.')[:2]
|
|
||||||
pyl_mw = self._parent
|
|
||||||
try:
|
|
||||||
data = data.select(station=station)
|
|
||||||
if not data:
|
|
||||||
self._warn('No data for station {}'.format(station))
|
|
||||||
return
|
|
||||||
pickDlg = PickDlg(self._parent, parameter=self.parameter,
|
|
||||||
data=data, network=network, station=station,
|
|
||||||
picks=self._parent.get_current_event().getPick(station),
|
|
||||||
autopicks=self._parent.get_current_event().getAutopick(station),
|
|
||||||
filteroptions=self._parent.filteroptions, metadata=self.metadata,
|
|
||||||
event=pyl_mw.get_current_event())
|
|
||||||
except Exception as e:
|
|
||||||
message = 'Could not generate Plot for station {st}.\n {er}'.format(st=station, er=e)
|
|
||||||
self._warn(message)
|
|
||||||
print(message, e)
|
|
||||||
print(traceback.format_exc())
|
|
||||||
return
|
|
||||||
try:
|
|
||||||
if pickDlg.exec_():
|
|
||||||
pyl_mw.setDirty(True)
|
|
||||||
pyl_mw.update_status('picks accepted ({0})'.format(station))
|
|
||||||
pyl_mw.addPicks(station, pickDlg.getPicks(picktype='manual'), type='manual')
|
|
||||||
pyl_mw.addPicks(station, pickDlg.getPicks(picktype='auto'), type='auto')
|
|
||||||
if self.auto_refresh_box.isChecked():
|
|
||||||
self._refresh_drawings()
|
|
||||||
else:
|
|
||||||
self.highlight_station(network, station, color='yellow')
|
|
||||||
pyl_mw.drawPicks(station)
|
|
||||||
pyl_mw.draw()
|
|
||||||
else:
|
|
||||||
pyl_mw.update_status('picks discarded ({0})'.format(station))
|
|
||||||
except Exception as e:
|
|
||||||
message = 'Could not save picks for station {st}.\n{er}'.format(st=station, er=e)
|
|
||||||
self._warn(message)
|
|
||||||
print(message, e)
|
|
||||||
print(traceback.format_exc())
|
|
||||||
|
|
||||||
def connectSignals(self):
|
|
||||||
self.comboBox_phase.currentIndexChanged.connect(self._refresh_drawings)
|
|
||||||
self.comboBox_am.currentIndexChanged.connect(self._refresh_drawings)
|
|
||||||
self.cmaps_box.currentIndexChanged.connect(self._refresh_drawings)
|
|
||||||
self.annotations_box.stateChanged.connect(self.switch_annotations)
|
|
||||||
self.refresh_button.clicked.connect(self._refresh_drawings)
|
|
||||||
self.canvas.mpl_connect('motion_notify_event', self.mouse_moved)
|
|
||||||
# self.zoom_id = self.basemap.ax.figure.canvas.mpl_connect('scroll_event', self.zoom)
|
|
||||||
|
|
||||||
def _from_dict(self, function, key):
|
def _from_dict(self, function, key):
|
||||||
return function(self.stations_dict.values(), key=lambda x: x[key])[key]
|
return function(self.stations_dict.values(), key=lambda x: x[key])[key]
|
||||||
|
|
||||||
@@ -205,14 +363,6 @@ class Array_map(QtGui.QWidget):
|
|||||||
def get_max_from_picks(self):
|
def get_max_from_picks(self):
|
||||||
return max(self.picks_rel.values())
|
return max(self.picks_rel.values())
|
||||||
|
|
||||||
def mouse_moved(self, event):
|
|
||||||
if not event.inaxes == self.main_ax:
|
|
||||||
return
|
|
||||||
x = event.xdata
|
|
||||||
y = event.ydata
|
|
||||||
lat, lon = self.basemap(x, y, inverse=True)
|
|
||||||
self.status_label.setText('Latitude: {}, Longitude: {}'.format(lat, lon))
|
|
||||||
|
|
||||||
def current_picks_dict(self):
|
def current_picks_dict(self):
|
||||||
picktype = self.comboBox_am.currentText().split(' ')[0]
|
picktype = self.comboBox_am.currentText().split(' ')[0]
|
||||||
auto_manu = {'auto': self.autopicks_dict,
|
auto_manu = {'auto': self.autopicks_dict,
|
||||||
@@ -220,57 +370,6 @@ class Array_map(QtGui.QWidget):
|
|||||||
'hybrid': self.hybrids_dict}
|
'hybrid': self.hybrids_dict}
|
||||||
return auto_manu[picktype]
|
return auto_manu[picktype]
|
||||||
|
|
||||||
def init_graphics(self):
|
|
||||||
if not self.figure:
|
|
||||||
self.figure = Figure()
|
|
||||||
|
|
||||||
self.status_label = QtGui.QLabel()
|
|
||||||
|
|
||||||
self.main_ax = self.figure.add_subplot(111)
|
|
||||||
#self.main_ax.set_facecolor('0.7')
|
|
||||||
self.canvas = PylotCanvas(self.figure, parent=self._parent, multicursor=True,
|
|
||||||
panZoomX=False, panZoomY=False)
|
|
||||||
|
|
||||||
self.main_box = QtGui.QVBoxLayout()
|
|
||||||
self.setLayout(self.main_box)
|
|
||||||
|
|
||||||
self.top_row = QtGui.QHBoxLayout()
|
|
||||||
self.main_box.addLayout(self.top_row, 1)
|
|
||||||
|
|
||||||
self.comboBox_phase = QtGui.QComboBox()
|
|
||||||
self.comboBox_phase.insertItem(0, 'P')
|
|
||||||
self.comboBox_phase.insertItem(1, 'S')
|
|
||||||
|
|
||||||
self.comboBox_am = QtGui.QComboBox()
|
|
||||||
self.comboBox_am.insertItem(0, 'hybrid (prefer manual)')
|
|
||||||
self.comboBox_am.insertItem(1, 'manual')
|
|
||||||
self.comboBox_am.insertItem(2, 'auto')
|
|
||||||
|
|
||||||
self.annotations_box = QtGui.QCheckBox('Annotate')
|
|
||||||
self.annotations_box.setChecked(True)
|
|
||||||
self.auto_refresh_box = QtGui.QCheckBox('Automatic refresh')
|
|
||||||
self.auto_refresh_box.setChecked(True)
|
|
||||||
self.refresh_button = QtGui.QPushButton('Refresh')
|
|
||||||
self.cmaps_box = QtGui.QComboBox()
|
|
||||||
self.cmaps_box.setMaxVisibleItems(20)
|
|
||||||
[self.cmaps_box.addItem(map_name) for map_name in sorted(plt.colormaps())]
|
|
||||||
# try to set to hsv as default
|
|
||||||
self.cmaps_box.setCurrentIndex(self.cmaps_box.findText('hsv'))
|
|
||||||
|
|
||||||
self.top_row.addWidget(QtGui.QLabel('Select a phase: '))
|
|
||||||
self.top_row.addWidget(self.comboBox_phase)
|
|
||||||
self.top_row.setStretch(1, 1) # set stretch of item 1 to 1
|
|
||||||
self.top_row.addWidget(QtGui.QLabel('Pick type: '))
|
|
||||||
self.top_row.addWidget(self.comboBox_am)
|
|
||||||
self.top_row.setStretch(3, 1) # set stretch of item 1 to 1
|
|
||||||
self.top_row.addWidget(self.cmaps_box)
|
|
||||||
self.top_row.addWidget(self.annotations_box)
|
|
||||||
self.top_row.addWidget(self.auto_refresh_box)
|
|
||||||
self.top_row.addWidget(self.refresh_button)
|
|
||||||
|
|
||||||
self.main_box.addWidget(self.canvas, 1)
|
|
||||||
self.main_box.addWidget(self.status_label, 0)
|
|
||||||
|
|
||||||
def init_stations(self):
|
def init_stations(self):
|
||||||
self.stations_dict = self.metadata.get_all_coordinates()
|
self.stations_dict = self.metadata.get_all_coordinates()
|
||||||
self.latmin = self.get_min_from_stations('latitude')
|
self.latmin = self.get_min_from_stations('latitude')
|
||||||
@@ -289,7 +388,14 @@ class Array_map(QtGui.QWidget):
|
|||||||
try:
|
try:
|
||||||
station_name = st_id.split('.')[-1]
|
station_name = st_id.split('.')[-1]
|
||||||
# current_picks_dict: auto or manual
|
# current_picks_dict: auto or manual
|
||||||
pick = self.current_picks_dict()[station_name][phase]
|
station_picks = self.current_picks_dict().get(station_name)
|
||||||
|
if not station_picks:
|
||||||
|
continue
|
||||||
|
for phase_hint, pick in station_picks.items():
|
||||||
|
if identifyPhaseID(phase_hint) == phase:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
continue
|
||||||
if pick['picker'] == 'auto':
|
if pick['picker'] == 'auto':
|
||||||
if not pick['spe']:
|
if not pick['spe']:
|
||||||
continue
|
continue
|
||||||
@@ -323,41 +429,6 @@ class Array_map(QtGui.QWidget):
|
|||||||
self.londim = self.lonmax - self.lonmin
|
self.londim = self.lonmax - self.lonmin
|
||||||
self.latdim = self.latmax - self.latmin
|
self.latdim = self.latmax - self.latmin
|
||||||
|
|
||||||
def init_x_y_dimensions(self):
|
|
||||||
# transformation of lat/lon to ax coordinate system
|
|
||||||
for st_id, coords in self.stations_dict.items():
|
|
||||||
lat, lon = coords['latitude'], coords['longitude']
|
|
||||||
coords['x'], coords['y'] = self.basemap(lon, lat)
|
|
||||||
|
|
||||||
self.xdim = self.get_max_from_stations('x') - self.get_min_from_stations('x')
|
|
||||||
self.ydim = self.get_max_from_stations('y') - self.get_min_from_stations('y')
|
|
||||||
|
|
||||||
def init_basemap(self, resolution='l'):
|
|
||||||
# basemap = Basemap(projection=projection, resolution = resolution, ax=self.main_ax)
|
|
||||||
basemap = Basemap(projection='lcc', resolution=resolution, ax=self.main_ax,
|
|
||||||
width=self.width, height=self.height,
|
|
||||||
lat_0=(self.latmin + self.latmax) / 2.,
|
|
||||||
lon_0=(self.lonmin + self.lonmax) / 2.)
|
|
||||||
|
|
||||||
# basemap.fillcontinents(color=None, lake_color='aqua',zorder=1)
|
|
||||||
basemap.drawmapboundary(zorder=2) # fill_color='darkblue')
|
|
||||||
basemap.shadedrelief(zorder=3)
|
|
||||||
basemap.drawcountries(zorder=4)
|
|
||||||
basemap.drawstates(zorder=5)
|
|
||||||
basemap.drawcoastlines(zorder=6)
|
|
||||||
# labels = [left,right,top,bottom]
|
|
||||||
parallels = np.arange(-90, 90, 5.)
|
|
||||||
parallels_small = np.arange(-90, 90, 2.5)
|
|
||||||
basemap.drawparallels(parallels_small, linewidth=0.5, zorder=7)
|
|
||||||
basemap.drawparallels(parallels, zorder=7)#, labels=[1, 1, 0, 0])
|
|
||||||
meridians = np.arange(-180, 180, 5.)
|
|
||||||
meridians_small = np.arange(-180, 180, 2.5)
|
|
||||||
basemap.drawmeridians(meridians_small, linewidth=0.5, zorder=7)
|
|
||||||
basemap.drawmeridians(meridians, zorder=7)#, labels=[0, 0, 1, 1])
|
|
||||||
self.basemap = basemap
|
|
||||||
self.figure._tight = True
|
|
||||||
self.figure.tight_layout()
|
|
||||||
|
|
||||||
def init_lat_lon_grid(self, nstep=250):
|
def init_lat_lon_grid(self, nstep=250):
|
||||||
# create a regular grid to display colormap
|
# create a regular grid to display colormap
|
||||||
lataxis = np.linspace(self.latmin, self.latmax, nstep)
|
lataxis = np.linspace(self.latmin, self.latmax, nstep)
|
||||||
@@ -367,8 +438,7 @@ class Array_map(QtGui.QWidget):
|
|||||||
def init_picksgrid(self):
|
def init_picksgrid(self):
|
||||||
picks, uncertainties, lats, lons = self.get_picks_lat_lon()
|
picks, uncertainties, lats, lons = self.get_picks_lat_lon()
|
||||||
try:
|
try:
|
||||||
self.picksgrid_active = griddata((lats, lons), picks, (self.latgrid, self.longrid),
|
self.picksgrid_active = griddata((lats, lons), picks, (self.latgrid, self.longrid), method='linear')
|
||||||
method='linear')
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self._warn('Could not init picksgrid: {}'.format(e))
|
self._warn('Could not init picksgrid: {}'.format(e))
|
||||||
|
|
||||||
@@ -382,16 +452,6 @@ class Array_map(QtGui.QWidget):
|
|||||||
longitudes.append(coords['longitude'])
|
longitudes.append(coords['longitude'])
|
||||||
return stations, latitudes, longitudes
|
return stations, latitudes, longitudes
|
||||||
|
|
||||||
def get_st_x_y_for_plot(self):
|
|
||||||
stations = []
|
|
||||||
xs = []
|
|
||||||
ys = []
|
|
||||||
for st_id, coords in self.stations_dict.items():
|
|
||||||
stations.append(st_id)
|
|
||||||
xs.append(coords['x'])
|
|
||||||
ys.append(coords['y'])
|
|
||||||
return stations, xs, ys
|
|
||||||
|
|
||||||
def get_picks_lat_lon(self):
|
def get_picks_lat_lon(self):
|
||||||
picks = []
|
picks = []
|
||||||
uncertainties = []
|
uncertainties = []
|
||||||
@@ -404,65 +464,85 @@ class Array_map(QtGui.QWidget):
|
|||||||
longitudes.append(self.stations_dict[st_id]['longitude'])
|
longitudes.append(self.stations_dict[st_id]['longitude'])
|
||||||
return picks, uncertainties, latitudes, longitudes
|
return picks, uncertainties, latitudes, longitudes
|
||||||
|
|
||||||
def draw_contour_filled(self, nlevel='100'):
|
# plotting -----------------------------------------------------
|
||||||
# self.test_gradient()
|
def highlight_station(self, network, station, color):
|
||||||
|
stat_dict = self.stations_dict['{}.{}'.format(network, station)]
|
||||||
|
lat = stat_dict['latitude']
|
||||||
|
lon = stat_dict['longitude']
|
||||||
|
self.highlighted_stations.append(self.canvas.axes.scatter(lon, lat, s=self.pointsize, edgecolors=color,
|
||||||
|
facecolors='none', zorder=12,
|
||||||
|
transform=ccrs.PlateCarree(), label='deleted'))
|
||||||
|
|
||||||
|
def openPickDlg(self, ind):
|
||||||
|
wfdata = self._parent.get_data().get_wf_data()
|
||||||
|
wfdata_comp = self._parent.get_data().get_wf_dataComp()
|
||||||
|
for index in ind:
|
||||||
|
network, station = self._station_onpick_ids[index].split('.')[:2]
|
||||||
|
pyl_mw = self._parent
|
||||||
|
try:
|
||||||
|
wfdata = wfdata.select(station=station)
|
||||||
|
wfdata_comp = wfdata_comp.select(station=station)
|
||||||
|
if not wfdata:
|
||||||
|
self._warn('No data for station {}'.format(station))
|
||||||
|
return
|
||||||
|
pickDlg = PickDlg(self._parent, parameter=self.parameter,
|
||||||
|
data=wfdata.copy(), data_compare=wfdata_comp.copy(), network=network, station=station,
|
||||||
|
picks=self._parent.get_current_event().getPick(station),
|
||||||
|
autopicks=self._parent.get_current_event().getAutopick(station),
|
||||||
|
filteroptions=self._parent.filteroptions, metadata=self.metadata,
|
||||||
|
model=self.parameter.get('taup_model'),
|
||||||
|
event=pyl_mw.get_current_event())
|
||||||
|
except Exception as e:
|
||||||
|
message = 'Could not generate Plot for station {st}.\n {er}'.format(st=station, er=e)
|
||||||
|
self._warn(message)
|
||||||
|
print(message, e)
|
||||||
|
print(traceback.format_exc())
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
if pickDlg.exec_():
|
||||||
|
pyl_mw.setDirty(True)
|
||||||
|
pyl_mw.update_status('picks accepted ({0})'.format(station))
|
||||||
|
pyl_mw.addPicks(station, pickDlg.getPicks(picktype='manual'), type='manual')
|
||||||
|
pyl_mw.addPicks(station, pickDlg.getPicks(picktype='auto'), type='auto')
|
||||||
|
if self.auto_refresh_box.isChecked():
|
||||||
|
self._refresh_drawings()
|
||||||
|
else:
|
||||||
|
self.highlight_station(network, station, color='yellow')
|
||||||
|
pyl_mw.drawPicks(station)
|
||||||
|
pyl_mw.draw()
|
||||||
|
else:
|
||||||
|
pyl_mw.update_status('picks discarded ({0})'.format(station))
|
||||||
|
except Exception as e:
|
||||||
|
message = 'Could not save picks for station {st}.\n{er}'.format(st=station, er=e)
|
||||||
|
self._warn(message)
|
||||||
|
print(message, e)
|
||||||
|
print(traceback.format_exc())
|
||||||
|
|
||||||
|
def draw_contour_filled(self, nlevel=50):
|
||||||
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.basemap.contour(self.longrid, self.latgrid, self.picksgrid_active, levels,
|
|
||||||
linewidths=self.linewidth, latlon=True, zorder=8, alpha=0.7,
|
self.contourf = self.canvas.axes.contourf(self.longrid, self.latgrid, self.picksgrid_active, levels,
|
||||||
cmap=self.get_colormap())
|
linewidths=self.linewidth * 5, transform=ccrs.PlateCarree(),
|
||||||
|
alpha=0.4, zorder=8, cmap=self.get_colormap())
|
||||||
|
|
||||||
def get_colormap(self):
|
def get_colormap(self):
|
||||||
return plt.get_cmap(self.cmaps_box.currentText())
|
return plt.get_cmap(self.cmaps_box.currentText())
|
||||||
|
|
||||||
def test_gradient(self):
|
|
||||||
st_ids = self.picks_rel.keys()
|
|
||||||
x, y = np.gradient(self.picksgrid_active)
|
|
||||||
gradient_modulus = np.sqrt(x ** 2 + y ** 2)
|
|
||||||
global_mean_gradient = np.nanmean(gradient_modulus)
|
|
||||||
delta_gradient = []
|
|
||||||
for st_id in st_ids:
|
|
||||||
pick_item = self.picks_rel.pop(st_id)
|
|
||||||
self.init_picksgrid()
|
|
||||||
x, y = np.gradient(self.picksgrid_active)
|
|
||||||
gradient_modulus = np.sqrt(x ** 2 + y ** 2)
|
|
||||||
mean_gradient = np.nanmean(gradient_modulus)
|
|
||||||
dgradient = global_mean_gradient - mean_gradient
|
|
||||||
# print('station: {}, mean gradient: {}'.format(st_id, dgradient))
|
|
||||||
delta_gradient.append(dgradient)
|
|
||||||
self.picks_rel[st_id] = pick_item
|
|
||||||
global_std_gradient = np.nanstd(delta_gradient)
|
|
||||||
marked_stations = []
|
|
||||||
for st_id, dg in zip(st_ids, delta_gradient):
|
|
||||||
if abs(dg) > global_std_gradient:
|
|
||||||
marked_stations.append(st_id)
|
|
||||||
self.marked_stations = marked_stations
|
|
||||||
self.init_picksgrid()
|
|
||||||
|
|
||||||
# fig = plt.figure()
|
|
||||||
# x = list(range(len(st_ids)))
|
|
||||||
# gradients = zip(x, delta_gradient)
|
|
||||||
# gradients.sort(key=lambda a: a[1])
|
|
||||||
# plt.plot(gradients[0], gradients[1])
|
|
||||||
|
|
||||||
# global_var_gradient = np.nanvar(delta_gradient)
|
|
||||||
# plt.plot(x, delta_gradient)
|
|
||||||
# plt.axhline(global_std_gradient, color='green')
|
|
||||||
# plt.axhline(2 * global_std_gradient, color='blue')
|
|
||||||
# plt.axhline(global_var_gradient, color='red')
|
|
||||||
# plt.xticks(x, st_ids)
|
|
||||||
# plt.show()
|
|
||||||
|
|
||||||
def scatter_all_stations(self):
|
def scatter_all_stations(self):
|
||||||
stations, lats, lons = self.get_st_lat_lon_for_plot()
|
stations, lats, lons = self.get_st_lat_lon_for_plot()
|
||||||
self.sc = self.basemap.scatter(lons, lats, s=self.pointsize, facecolor='none', latlon=True, marker='.',
|
|
||||||
zorder=10, picker=True, edgecolor='0.5', label='Not Picked')
|
self.sc = self.canvas.axes.scatter(lons, lats, s=self.pointsize * 3, facecolor='none', marker='.',
|
||||||
self.cid = self.canvas.mpl_connect('pick_event', self.onpick)
|
zorder=10, picker=True, edgecolor='0.5', label='Not Picked',
|
||||||
|
transform=ccrs.PlateCarree())
|
||||||
|
|
||||||
|
self.cid = self.plotWidget.mpl_connect('pick_event', self.onpick)
|
||||||
self._station_onpick_ids = stations
|
self._station_onpick_ids = stations
|
||||||
if self.eventLoc:
|
if self.eventLoc:
|
||||||
lats, lons = self.eventLoc
|
lats, lons = self.eventLoc
|
||||||
self.sc_event = self.basemap.scatter(lons, lats, s=2*self.pointsize, facecolor='red',
|
self.sc_event = self.canvas.axes.scatter(lons, lats, s=5 * self.pointsize, facecolor='red', zorder=11,
|
||||||
latlon=True, zorder=11, label='Event (might be outside map region)')
|
label='Event (might be outside map region)', marker='*',
|
||||||
|
edgecolors='black',
|
||||||
|
transform=ccrs.PlateCarree())
|
||||||
|
|
||||||
def scatter_picked_stations(self):
|
def scatter_picked_stations(self):
|
||||||
picks, uncertainties, lats, lons = self.get_picks_lat_lon()
|
picks, uncertainties, lats, lons = self.get_picks_lat_lon()
|
||||||
@@ -471,24 +551,16 @@ class Array_map(QtGui.QWidget):
|
|||||||
|
|
||||||
phase = self.comboBox_phase.currentText()
|
phase = self.comboBox_phase.currentText()
|
||||||
timeerrors = self.parameter['timeerrors{}'.format(phase)]
|
timeerrors = self.parameter['timeerrors{}'.format(phase)]
|
||||||
sizes = np.array([self.pointsize * ((5. - get_quality_class(uncertainty, timeerrors)))
|
sizes = np.array([self.pointsize * (5. - get_quality_class(uncertainty, timeerrors))
|
||||||
for uncertainty in uncertainties])
|
for uncertainty in uncertainties])
|
||||||
|
|
||||||
cmap = self.get_colormap()
|
cmap = self.get_colormap()
|
||||||
# workaround because of an issue with latlon transformation of arrays with len <3
|
self.sc_picked = self.canvas.axes.scatter(lons, lats, s=sizes, edgecolors='white', cmap=cmap,
|
||||||
if len(lons) <= 2 and len(lats) <= 2:
|
c=picks, zorder=11, label='Picked', transform=ccrs.PlateCarree())
|
||||||
self.sc_picked = self.basemap.scatter(lons[0], lats[0], s=sizes, edgecolors='white', cmap=cmap,
|
|
||||||
c=picks[0], latlon=True, zorder=11)
|
|
||||||
if len(lons) == 2 and len(lats) == 2:
|
|
||||||
self.sc_picked = self.basemap.scatter(lons[1], lats[1], s=sizes, edgecolors='white', cmap=cmap,
|
|
||||||
c=picks[1], latlon=True, zorder=11)
|
|
||||||
if len(lons) > 2 and len(lats) > 2:
|
|
||||||
self.sc_picked = self.basemap.scatter(lons, lats, s=sizes, edgecolors='white', cmap=cmap,
|
|
||||||
c=picks, latlon=True, zorder=11, label='Picked')
|
|
||||||
|
|
||||||
def annotate_ax(self):
|
def annotate_ax(self):
|
||||||
self.annotations = []
|
self.annotations = []
|
||||||
stations, xs, ys = self.get_st_x_y_for_plot()
|
stations, ys, xs = self.get_st_lat_lon_for_plot()
|
||||||
# MP MP testing station highlighting if they have high impact on mean gradient of color map
|
# MP MP testing station highlighting if they have high impact on mean gradient of color map
|
||||||
# if self.picks_rel:
|
# if self.picks_rel:
|
||||||
# self.test_gradient()
|
# self.test_gradient()
|
||||||
@@ -501,16 +573,21 @@ class Array_map(QtGui.QWidget):
|
|||||||
color = 'lightgrey'
|
color = 'lightgrey'
|
||||||
if st in self.marked_stations:
|
if st in self.marked_stations:
|
||||||
color = 'red'
|
color = 'red'
|
||||||
self.annotations.append(self.main_ax.annotate(' %s' % st, xy=(x, y),
|
self.annotations.append(
|
||||||
fontsize=self.pointsize/4., fontweight='semibold',
|
self.canvas.axes.annotate(' %s' % st, xy=(x + 0.003, y + 0.003), fontsize=self.pointsize / 4.,
|
||||||
color=color, zorder=14))
|
fontweight='semibold', color=color, alpha=0.8,
|
||||||
self.legend = self.main_ax.legend(loc=1)
|
transform=ccrs.PlateCarree(), zorder=14,
|
||||||
self.legend.get_frame().set_facecolor((1, 1, 1, 0.75))
|
path_effects=[PathEffects.withStroke(
|
||||||
|
linewidth=self.pointsize / 15., foreground='k')]))
|
||||||
|
|
||||||
|
self.legend = self.canvas.axes.legend(loc=1, framealpha=1)
|
||||||
|
self.legend.set_zorder(100)
|
||||||
|
self.legend.get_frame().set_facecolor((1, 1, 1, 0.95))
|
||||||
|
|
||||||
def add_cbar(self, label):
|
def add_cbar(self, label):
|
||||||
self.cbax_bg = inset_axes(self.main_ax, width="6%", height="75%", loc=5)
|
self.cbax_bg = inset_axes(self.canvas.axes, width="6%", height="75%", loc=5)
|
||||||
cbax = inset_axes(self.main_ax, width='2%', height='70%', loc=5)
|
cbax = inset_axes(self.canvas.axes, width='2%', height='70%', loc=5)
|
||||||
cbar = self.main_ax.figure.colorbar(self.sc_picked, cax=cbax)
|
cbar = self.canvas.axes.figure.colorbar(self.sc_picked, cax=cbax)
|
||||||
cbar.set_label(label)
|
cbar.set_label(label)
|
||||||
cbax.yaxis.tick_left()
|
cbax.yaxis.tick_left()
|
||||||
cbax.yaxis.set_label_position('left')
|
cbax.yaxis.set_label_position('left')
|
||||||
@@ -521,6 +598,7 @@ class Array_map(QtGui.QWidget):
|
|||||||
self.cbax_bg.patch.set_facecolor((1, 1, 1, 0.75))
|
self.cbax_bg.patch.set_facecolor((1, 1, 1, 0.75))
|
||||||
return cbar
|
return cbar
|
||||||
|
|
||||||
|
# handle drawings -----------------------------------------------------
|
||||||
def refresh_drawings(self, picks=None, autopicks=None):
|
def refresh_drawings(self, picks=None, autopicks=None):
|
||||||
self.picks_dict = picks
|
self.picks_dict = picks
|
||||||
self.autopicks_dict = autopicks
|
self.autopicks_dict = autopicks
|
||||||
@@ -560,7 +638,7 @@ class Array_map(QtGui.QWidget):
|
|||||||
self.comboBox_phase.setEnabled(False)
|
self.comboBox_phase.setEnabled(False)
|
||||||
if self.annotate:
|
if self.annotate:
|
||||||
self.annotate_ax()
|
self.annotate_ax()
|
||||||
self.canvas.draw()
|
self.plotWidget.draw_idle()
|
||||||
|
|
||||||
def remove_drawings(self):
|
def remove_drawings(self):
|
||||||
self.remove_annotations()
|
self.remove_annotations()
|
||||||
@@ -584,7 +662,7 @@ class Array_map(QtGui.QWidget):
|
|||||||
self.remove_contourf()
|
self.remove_contourf()
|
||||||
del self.contourf
|
del self.contourf
|
||||||
if hasattr(self, 'cid'):
|
if hasattr(self, 'cid'):
|
||||||
self.canvas.mpl_disconnect(self.cid)
|
self.plotWidget.mpl_disconnect(self.cid)
|
||||||
del self.cid
|
del self.cid
|
||||||
try:
|
try:
|
||||||
self.sc.remove()
|
self.sc.remove()
|
||||||
@@ -594,7 +672,7 @@ class Array_map(QtGui.QWidget):
|
|||||||
self.legend.remove()
|
self.legend.remove()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print('Warning: could not remove legend. Reason: {}'.format(e))
|
print('Warning: could not remove legend. Reason: {}'.format(e))
|
||||||
self.canvas.draw()
|
self.plotWidget.draw_idle()
|
||||||
|
|
||||||
def remove_contourf(self):
|
def remove_contourf(self):
|
||||||
for item in self.contourf.collections:
|
for item in self.contourf.collections:
|
||||||
@@ -605,34 +683,16 @@ class Array_map(QtGui.QWidget):
|
|||||||
annotation.remove()
|
annotation.remove()
|
||||||
self.annotations = []
|
self.annotations = []
|
||||||
|
|
||||||
def zoom(self, event):
|
def saveFigure(self):
|
||||||
map = self.basemap
|
if self.canvas.fig:
|
||||||
xlim = map.ax.get_xlim()
|
fd = QtWidgets.QFileDialog()
|
||||||
ylim = map.ax.get_ylim()
|
fname, filter = fd.getSaveFileName(self.parent(), filter='Images (*.png *.svg *.jpg)')
|
||||||
x, y = event.xdata, event.ydata
|
if not fname:
|
||||||
zoom = {'up': 1. / 2.,
|
|
||||||
'down': 2.}
|
|
||||||
|
|
||||||
if not event.xdata or not event.ydata:
|
|
||||||
return
|
return
|
||||||
|
if not any([fname.endswith(item) for item in ['.png', '.svg', '.jpg']]):
|
||||||
if event.button in zoom:
|
fname += '.png'
|
||||||
factor = zoom[event.button]
|
self.canvas.fig.savefig(fname)
|
||||||
xdiff = (xlim[1] - xlim[0]) * factor
|
|
||||||
xl = x - 0.5 * xdiff
|
|
||||||
xr = x + 0.5 * xdiff
|
|
||||||
ydiff = (ylim[1] - ylim[0]) * factor
|
|
||||||
yb = y - 0.5 * ydiff
|
|
||||||
yt = y + 0.5 * ydiff
|
|
||||||
|
|
||||||
if xl < map.xmin or yb < map.ymin or xr > map.xmax or yt > map.ymax:
|
|
||||||
xl, xr = map.xmin, map.xmax
|
|
||||||
yb, yt = map.ymin, map.ymax
|
|
||||||
map.ax.set_xlim(xl, xr)
|
|
||||||
map.ax.set_ylim(yb, yt)
|
|
||||||
map.ax.figure.canvas.draw()
|
|
||||||
|
|
||||||
def _warn(self, message):
|
def _warn(self, message):
|
||||||
self.qmb = QtGui.QMessageBox(QtGui.QMessageBox.Icon.Warning,
|
self.qmb = QtWidgets.QMessageBox(QtWidgets.QMessageBox.Icon.Warning, 'Warning', message)
|
||||||
'Warning', message)
|
|
||||||
self.qmb.show()
|
self.qmb.show()
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -2,9 +2,11 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
import glob
|
import glob
|
||||||
import numpy as np
|
import logging
|
||||||
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 +48,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
|
||||||
@@ -83,10 +85,10 @@ class Metadata(object):
|
|||||||
print('Path {} not in inventories list.'.format(path_to_inventory))
|
print('Path {} not in inventories list.'.format(path_to_inventory))
|
||||||
return
|
return
|
||||||
self.inventories.remove(path_to_inventory)
|
self.inventories.remove(path_to_inventory)
|
||||||
for filename in self.inventory_files.keys():
|
for filename in list(self.inventory_files.keys()):
|
||||||
if filename.startswith(path_to_inventory):
|
if filename.startswith(path_to_inventory):
|
||||||
del (self.inventory_files[filename])
|
del (self.inventory_files[filename])
|
||||||
for seed_id in self.seed_ids.keys():
|
for seed_id in list(self.seed_ids.keys()):
|
||||||
if self.seed_ids[seed_id].startswith(path_to_inventory):
|
if self.seed_ids[seed_id].startswith(path_to_inventory):
|
||||||
del (self.seed_ids[seed_id])
|
del (self.seed_ids[seed_id])
|
||||||
# have to clean self.stations_dict as well
|
# have to clean self.stations_dict as well
|
||||||
@@ -188,7 +190,11 @@ class Metadata(object):
|
|||||||
metadata = self.get_metadata(seed_id, time)
|
metadata = self.get_metadata(seed_id, time)
|
||||||
if not metadata:
|
if not metadata:
|
||||||
return
|
return
|
||||||
|
try:
|
||||||
return metadata['data'].get_coordinates(seed_id, time)
|
return metadata['data'].get_coordinates(seed_id, time)
|
||||||
|
# no specific exception defined in obspy inventory
|
||||||
|
except Exception as e:
|
||||||
|
logging.warning(f'Could not get metadata for {seed_id}')
|
||||||
|
|
||||||
def get_all_coordinates(self):
|
def get_all_coordinates(self):
|
||||||
def stat_info_from_parser(parser):
|
def stat_info_from_parser(parser):
|
||||||
@@ -211,6 +217,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 +276,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():
|
||||||
@@ -331,19 +338,6 @@ class Metadata(object):
|
|||||||
return inv, exc
|
return inv, exc
|
||||||
|
|
||||||
|
|
||||||
def time_from_header(header):
|
|
||||||
"""
|
|
||||||
Function takes in the second line from a .gse file and takes out the date and time from that line.
|
|
||||||
:param header: second line from .gse file
|
|
||||||
:type header: string
|
|
||||||
:return: a list of integers of form [year, month, day, hour, minute, second, microsecond]
|
|
||||||
"""
|
|
||||||
timeline = header.split(' ')
|
|
||||||
time = timeline[1].split('/') + timeline[2].split(':')
|
|
||||||
time = time[:-1] + time[-1].split('.')
|
|
||||||
return [int(t) for t in time]
|
|
||||||
|
|
||||||
|
|
||||||
def check_time(datetime):
|
def check_time(datetime):
|
||||||
"""
|
"""
|
||||||
Function takes in date and time as list and validates it's values by trying to make an UTCDateTime object from it
|
Function takes in date and time as list and validates it's values by trying to make an UTCDateTime object from it
|
||||||
@@ -351,25 +345,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
|
||||||
@@ -381,166 +375,6 @@ def check_time(datetime):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def get_file_list(root_dir):
|
|
||||||
"""
|
|
||||||
Function uses a directorie to get all the *.gse files from it.
|
|
||||||
:param root_dir: a directorie leading to the .gse files
|
|
||||||
:type root_dir: string
|
|
||||||
:return: returns a list of filenames (without path to them)
|
|
||||||
"""
|
|
||||||
file_list = glob.glob1(root_dir, '*.gse')
|
|
||||||
return file_list
|
|
||||||
|
|
||||||
|
|
||||||
def checks_station_second(datetime, file):
|
|
||||||
"""
|
|
||||||
Function uses the given list to check if the parameter 'second' is set to 60 by mistake
|
|
||||||
and sets the time correctly if so. Can only correct time if no date change would be necessary.
|
|
||||||
:param datetime: [year, month, day, hour, minute, second, microsecond]
|
|
||||||
:return: returns the input with the correct value for second
|
|
||||||
"""
|
|
||||||
if datetime[5] == 60:
|
|
||||||
if datetime[4] == 59:
|
|
||||||
if datetime[3] == 23:
|
|
||||||
err_msg = 'Date should be next day. ' \
|
|
||||||
'File not changed: {0}'.format(file)
|
|
||||||
raise ValueError(err_msg)
|
|
||||||
else:
|
|
||||||
datetime[3] += 1
|
|
||||||
datetime[4] = 0
|
|
||||||
datetime[5] = 0
|
|
||||||
else:
|
|
||||||
datetime[4] += 1
|
|
||||||
datetime[5] = 0
|
|
||||||
return datetime
|
|
||||||
|
|
||||||
|
|
||||||
def make_time_line(line, datetime):
|
|
||||||
"""
|
|
||||||
Function takes in the original line from a .gse file and a list of date and
|
|
||||||
time values to make a new line with corrected date and time.
|
|
||||||
:param line: second line from .gse file.
|
|
||||||
:type line: string
|
|
||||||
:param datetime: list of integers [year, month, day, hour, minute, second, microsecond]
|
|
||||||
:type datetime: list
|
|
||||||
:return: returns a string to write it into a file.
|
|
||||||
"""
|
|
||||||
ins_form = '{0:02d}:{1:02d}:{2:02d}.{3:03d}'
|
|
||||||
insertion = ins_form.format(int(datetime[3]),
|
|
||||||
int(datetime[4]),
|
|
||||||
int(datetime[5]),
|
|
||||||
int(datetime[6] * 1e-3))
|
|
||||||
newline = line[:16] + insertion + line[28:]
|
|
||||||
return newline
|
|
||||||
|
|
||||||
|
|
||||||
def evt_head_check(root_dir, out_dir=None):
|
|
||||||
"""
|
|
||||||
A function to make sure that an arbitrary number of .gse files have correct values in their header.
|
|
||||||
:param root_dir: a directory leading to the .gse files.
|
|
||||||
:type root_dir: string
|
|
||||||
:param out_dir: a directory to store the new files somwhere els.
|
|
||||||
:return: returns nothing
|
|
||||||
"""
|
|
||||||
if not out_dir:
|
|
||||||
print('WARNING files are going to be overwritten!')
|
|
||||||
inp = str(raw_input('Continue? [y/N]'))
|
|
||||||
if not inp == 'y':
|
|
||||||
sys.exit()
|
|
||||||
filelist = get_file_list(root_dir)
|
|
||||||
nfiles = 0
|
|
||||||
for file in filelist:
|
|
||||||
infile = open(os.path.join(root_dir, file), 'r')
|
|
||||||
lines = infile.readlines()
|
|
||||||
infile.close()
|
|
||||||
datetime = time_from_header(lines[1])
|
|
||||||
if check_time(datetime):
|
|
||||||
continue
|
|
||||||
else:
|
|
||||||
nfiles += 1
|
|
||||||
datetime = checks_station_second(datetime, file)
|
|
||||||
print('writing ' + file)
|
|
||||||
# write File
|
|
||||||
lines[1] = make_time_line(lines[1], datetime)
|
|
||||||
if not out_dir:
|
|
||||||
out = open(os.path.join(root_dir, file), 'w')
|
|
||||||
out.writelines(lines)
|
|
||||||
out.close()
|
|
||||||
else:
|
|
||||||
out = open(os.path.join(out_dir, file), 'w')
|
|
||||||
out.writelines(lines)
|
|
||||||
out.close()
|
|
||||||
print(nfiles)
|
|
||||||
|
|
||||||
|
|
||||||
def read_metadata(path_to_inventory):
|
|
||||||
"""
|
|
||||||
take path_to_inventory and return either the corresponding list of files
|
|
||||||
found or the Parser object for a network dataless seed volume to prevent
|
|
||||||
read overhead for large dataless seed volumes
|
|
||||||
:param path_to_inventory:
|
|
||||||
:return: tuple containing a either list of files or `obspy.io.xseed.Parser`
|
|
||||||
object and the inventory type found
|
|
||||||
:rtype: tuple
|
|
||||||
"""
|
|
||||||
dlfile = list()
|
|
||||||
invfile = list()
|
|
||||||
respfile = list()
|
|
||||||
# possible file extensions specified here:
|
|
||||||
inv = dict(dless=dlfile, xml=invfile, resp=respfile, dseed=dlfile[:])
|
|
||||||
if os.path.isfile(path_to_inventory):
|
|
||||||
ext = os.path.splitext(path_to_inventory)[1].split('.')[1]
|
|
||||||
inv[ext] += [path_to_inventory]
|
|
||||||
else:
|
|
||||||
for ext in inv.keys():
|
|
||||||
inv[ext] += glob.glob1(path_to_inventory, '*.{0}'.format(ext))
|
|
||||||
|
|
||||||
invtype = key_for_set_value(inv)
|
|
||||||
|
|
||||||
if invtype is None:
|
|
||||||
print("Neither dataless-SEED file, inventory-xml file nor "
|
|
||||||
"RESP-file found!")
|
|
||||||
print("!!WRONG CALCULATION OF SOURCE PARAMETERS!!")
|
|
||||||
robj = None,
|
|
||||||
elif invtype == 'dless': # prevent multiple read of large dlsv
|
|
||||||
print("Reading metadata information from dataless-SEED file ...")
|
|
||||||
if len(inv[invtype]) == 1:
|
|
||||||
fullpath_inv = os.path.join(path_to_inventory, inv[invtype][0])
|
|
||||||
robj = Parser(fullpath_inv)
|
|
||||||
else:
|
|
||||||
robj = inv[invtype]
|
|
||||||
else:
|
|
||||||
print("Reading metadata information from inventory-xml file ...")
|
|
||||||
robj = read_inventory(inv[invtype])
|
|
||||||
return invtype, robj
|
|
||||||
|
|
||||||
|
|
||||||
# idea to optimize read_metadata
|
|
||||||
# def read_metadata_new(path_to_inventory):
|
|
||||||
# metadata_objects = []
|
|
||||||
# # read multiple files from directory
|
|
||||||
# if os.path.isdir(path_to_inventory):
|
|
||||||
# fnames = os.listdir(path_to_inventory)
|
|
||||||
# # read single file
|
|
||||||
# elif os.path.isfile(path_to_inventory):
|
|
||||||
# fnames = [path_to_inventory]
|
|
||||||
# else:
|
|
||||||
# print("Neither dataless-SEED file, inventory-xml file nor "
|
|
||||||
# "RESP-file found!")
|
|
||||||
# print("!!WRONG CALCULATION OF SOURCE PARAMETERS!!")
|
|
||||||
# fnames = []
|
|
||||||
#
|
|
||||||
# for fname in fnames:
|
|
||||||
# path_to_inventory_filename = os.path.join(path_to_inventory, fname)
|
|
||||||
# try:
|
|
||||||
# ftype, robj = read_metadata_file(path_to_inventory_filename)
|
|
||||||
# metadata_objects.append((ftype, robj))
|
|
||||||
# except Exception as e:
|
|
||||||
# print('Could not read metadata file {} '
|
|
||||||
# 'because of the following Exception: {}'.format(path_to_inventory_filename, e))
|
|
||||||
# return metadata_objects
|
|
||||||
|
|
||||||
|
|
||||||
def restitute_trace(input_tuple):
|
def restitute_trace(input_tuple):
|
||||||
def no_metadata(tr, seed_id):
|
def no_metadata(tr, seed_id):
|
||||||
print('no metadata file found '
|
print('no metadata file found '
|
||||||
|
|||||||
@@ -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',
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -21,12 +22,13 @@ class Event(ObsPyEvent):
|
|||||||
:param path: path to event directory
|
:param path: path to event directory
|
||||||
:type path: str
|
:type path: str
|
||||||
"""
|
"""
|
||||||
|
# TODO: remove rootpath and database
|
||||||
self.pylot_id = path.split('/')[-1]
|
self.pylot_id = path.split('/')[-1]
|
||||||
# initialize super class
|
# initialize super class
|
||||||
super(Event, self).__init__(resource_id=ResourceIdentifier('smi:local/' + self.pylot_id))
|
super(Event, self).__init__(resource_id=ResourceIdentifier('smi:local/' + self.pylot_id))
|
||||||
self.path = path
|
self.path = path
|
||||||
self.database = path.split('/')[-2]
|
self.database = path.split('/')[-2]
|
||||||
self.datapath = path.split('/')[-3]
|
self.datapath = os.path.split(path)[0] # path.split('/')[-3]
|
||||||
self.rootpath = '/' + os.path.join(*path.split('/')[:-3])
|
self.rootpath = '/' + os.path.join(*path.split('/')[:-3])
|
||||||
self.pylot_autopicks = {}
|
self.pylot_autopicks = {}
|
||||||
self.pylot_picks = {}
|
self.pylot_picks = {}
|
||||||
@@ -73,7 +75,7 @@ class Event(ObsPyEvent):
|
|||||||
text = lines[0]
|
text = lines[0]
|
||||||
self.addNotes(text)
|
self.addNotes(text)
|
||||||
try:
|
try:
|
||||||
datetime = UTCDateTime(path.split('/')[-1])
|
datetime = UTCDateTime(self.path.split('/')[-1])
|
||||||
origin = Origin(resource_id=self.resource_id, time=datetime, latitude=0, longitude=0, depth=0)
|
origin = Origin(resource_id=self.resource_id, time=datetime, latitude=0, longitude=0, depth=0)
|
||||||
self.origins.append(origin)
|
self.origins.append(origin)
|
||||||
except:
|
except:
|
||||||
@@ -296,13 +298,13 @@ class Event(ObsPyEvent):
|
|||||||
:rtype: None
|
:rtype: None
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
import cPickle
|
import pickle
|
||||||
except ImportError:
|
except ImportError:
|
||||||
import _pickle as cPickle
|
import _pickle as pickle
|
||||||
|
|
||||||
try:
|
try:
|
||||||
outfile = open(filename, 'wb')
|
outfile = open(filename, 'wb')
|
||||||
cPickle.dump(self, outfile, -1)
|
pickle.dump(self, outfile, -1)
|
||||||
self.dirty = False
|
self.dirty = False
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print('Could not pickle PyLoT event. Reason: {}'.format(e))
|
print('Could not pickle PyLoT event. Reason: {}'.format(e))
|
||||||
@@ -317,11 +319,11 @@ class Event(ObsPyEvent):
|
|||||||
:rtype: Event
|
:rtype: Event
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
import cPickle
|
import pickle
|
||||||
except ImportError:
|
except ImportError:
|
||||||
import _pickle as cPickle
|
import _pickle as pickle
|
||||||
infile = open(filename, 'rb')
|
infile = open(filename, 'rb')
|
||||||
event = cPickle.load(infile)
|
event = pickle.load(infile)
|
||||||
event.dirty = False
|
event.dirty = False
|
||||||
print('Loaded %s' % filename)
|
print('Loaded %s' % filename)
|
||||||
return event
|
return event
|
||||||
|
|||||||
@@ -3,24 +3,37 @@
|
|||||||
# 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
|
||||||
import multiprocessing
|
|
||||||
|
|
||||||
from PyLoT import Project
|
num_thread = "16"
|
||||||
|
os.environ["OMP_NUM_THREADS"] = num_thread
|
||||||
|
os.environ["OPENBLAS_NUM_THREADS"] = num_thread
|
||||||
|
os.environ["MKL_NUM_THREADS"] = num_thread
|
||||||
|
os.environ["VECLIB_MAXIMUM_THREADS"] = num_thread
|
||||||
|
os.environ["NUMEXPR_NUM_THREADS"] = num_thread
|
||||||
|
os.environ["NUMEXPR_MAX_THREADS"] = num_thread
|
||||||
|
|
||||||
|
import multiprocessing
|
||||||
|
import sys
|
||||||
|
import glob
|
||||||
|
import matplotlib
|
||||||
|
|
||||||
|
matplotlib.use('Qt5Agg')
|
||||||
|
sys.path.append(os.path.join('/'.join(sys.argv[0].split('/')[:-1]), '../../..'))
|
||||||
|
|
||||||
|
from pylot.core.io.project import Project
|
||||||
from pylot.core.util.dataprocessing import Metadata
|
from pylot.core.util.dataprocessing import Metadata
|
||||||
from pylot.core.util.array_map import Array_map
|
from pylot.core.util.array_map import Array_map
|
||||||
|
|
||||||
import matplotlib.pyplot as plt
|
import matplotlib.pyplot as plt
|
||||||
|
import argparse
|
||||||
|
|
||||||
|
|
||||||
def main(project_file_path, manual=False, auto=True, file_format='png', f_ext='', ncores=None):
|
def main(project_file_path, manual=False, auto=True, file_format='png', f_ext='', ncores=None):
|
||||||
project = Project.load(project_file_path)
|
project = Project.load(project_file_path)
|
||||||
nEvents = len(project.eventlist)
|
nEvents = len(project.eventlist)
|
||||||
input_list = []
|
input_list = []
|
||||||
|
print('\n')
|
||||||
for index, event in enumerate(project.eventlist):
|
for index, event in enumerate(project.eventlist):
|
||||||
# MP MP TESTING +++
|
|
||||||
#if not eventdir.endswith('20170908_044946.a'):
|
|
||||||
# continue
|
|
||||||
# MP MP ----
|
|
||||||
kwargs = dict(project=project, event=event, nEvents=nEvents, index=index, manual=manual, auto=auto,
|
kwargs = dict(project=project, event=event, nEvents=nEvents, index=index, manual=manual, auto=auto,
|
||||||
file_format=file_format, f_ext=f_ext)
|
file_format=file_format, f_ext=f_ext)
|
||||||
input_list.append(kwargs)
|
input_list.append(kwargs)
|
||||||
@@ -29,15 +42,21 @@ def main(project_file_path, manual=False, auto=True, file_format='png', f_ext=''
|
|||||||
for item in input_list:
|
for item in input_list:
|
||||||
array_map_worker(item)
|
array_map_worker(item)
|
||||||
else:
|
else:
|
||||||
pool = multiprocessing.Pool(ncores)
|
pool = multiprocessing.Pool(ncores, maxtasksperchild=1000)
|
||||||
result = pool.map(array_map_worker, input_list)
|
pool.map(array_map_worker, input_list)
|
||||||
pool.close()
|
pool.close()
|
||||||
pool.join()
|
pool.join()
|
||||||
|
|
||||||
|
|
||||||
def array_map_worker(input_dict):
|
def array_map_worker(input_dict):
|
||||||
event = input_dict['event']
|
event = input_dict['event']
|
||||||
eventdir = event.path
|
eventdir = event.path
|
||||||
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'))
|
||||||
|
if not len(xml_picks):
|
||||||
|
print('Event {} does not have any picks associated with event file extension {}'.format(eventdir,
|
||||||
|
input_dict['f_ext']))
|
||||||
|
return
|
||||||
# check for picks
|
# check for picks
|
||||||
manualpicks = event.getPicks()
|
manualpicks = event.getPicks()
|
||||||
autopicks = event.getAutopicks()
|
autopicks = event.getAutopicks()
|
||||||
@@ -51,11 +70,12 @@ def array_map_worker(input_dict):
|
|||||||
continue
|
continue
|
||||||
if not metadata:
|
if not metadata:
|
||||||
metadata = Metadata(inventory=metadata_path, verbosity=0)
|
metadata = Metadata(inventory=metadata_path, verbosity=0)
|
||||||
|
|
||||||
# create figure to plot on
|
# create figure to plot on
|
||||||
fig = plt.figure(figsize=(16,9))
|
fig, ax = plt.subplots(figsize=(15, 9))
|
||||||
# create array map object
|
# create array map object
|
||||||
map = Array_map(None, metadata, parameter=input_dict['project'].parameter, figure=fig,
|
map = Array_map(None, metadata, parameter=input_dict['project'].parameter, axes=ax,
|
||||||
width=2.13e6, height=1.2e6, pointsize=15., linewidth=1.0)
|
width=2.13e6, height=1.2e6, pointsize=25., linewidth=1.0)
|
||||||
# set combobox to auto/manual to plot correct pick type
|
# set combobox to auto/manual to plot correct pick type
|
||||||
map.comboBox_am.setCurrentIndex(map.comboBox_am.findText(pick_type))
|
map.comboBox_am.setCurrentIndex(map.comboBox_am.findText(pick_type))
|
||||||
# add picks to map and save file
|
# add picks to map and save file
|
||||||
@@ -65,11 +85,13 @@ def array_map_worker(input_dict):
|
|||||||
fig.savefig(fpath_out, dpi=300.)
|
fig.savefig(fpath_out, dpi=300.)
|
||||||
print('Wrote file: {}'.format(fpath_out))
|
print('Wrote file: {}'.format(fpath_out))
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
dataroot = '/home/marcel'
|
|
||||||
infiles=['alparray_all_events_0.03-0.1_mantle_correlated_v3.plp']
|
|
||||||
|
|
||||||
for infile in infiles:
|
if __name__ == '__main__':
|
||||||
main(os.path.join(dataroot, infile), f_ext='_correlated_0.1Hz', ncores=10)
|
cl = argparse.ArgumentParser()
|
||||||
#main('E:\Shared\AlpArray\\test_aa.plp', f_ext='_correlated_0.5Hz', ncores=1)
|
cl.add_argument('--dataroot', help='Directory containing the PyLoT .plp file', type=str)
|
||||||
#main('/home/marcel/alparray_m6.5-6.9_mantle_correlated_v3.plp', f_ext='_correlated_0.5Hz')
|
cl.add_argument('--infiles', help='.plp files to use', nargs='+')
|
||||||
|
cl.add_argument('--ncores', hepl='Specify number of parallel processes', type=int, default=1)
|
||||||
|
args = cl.parse_args()
|
||||||
|
|
||||||
|
for infile in args.infiles:
|
||||||
|
main(os.path.join(args.dataroot, infile), f_ext='_correlated_0.03-0.1', ncores=args.ncores)
|
||||||
|
|||||||
@@ -7,11 +7,10 @@ try:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
print('Warning: Could not import module pyqtgraph.')
|
print('Warning: Could not import module pyqtgraph.')
|
||||||
try:
|
try:
|
||||||
from PySide import QtCore
|
from PySide2 import QtCore
|
||||||
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
|
||||||
|
|
||||||
|
|
||||||
@@ -49,7 +48,7 @@ def which(program, parameter):
|
|||||||
:rtype: str
|
:rtype: str
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
from PySide.QtCore import QSettings
|
from PySide2.QtCore import QSettings
|
||||||
settings = QSettings()
|
settings = QSettings()
|
||||||
for key in settings.allKeys():
|
for key in settings.allKeys():
|
||||||
if 'binPath' in key:
|
if 'binPath' in key:
|
||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from obspy import UTCDateTime
|
from obspy import UTCDateTime
|
||||||
|
|
||||||
|
|
||||||
@@ -34,17 +35,25 @@ def qml_from_obspyDMT(path):
|
|||||||
|
|
||||||
if not os.path.exists(path):
|
if not os.path.exists(path):
|
||||||
return IOError('Could not find Event at {}'.format(path))
|
return IOError('Could not find Event at {}'.format(path))
|
||||||
infile = open(path, 'rb')
|
|
||||||
event_dmt = pickle.load(infile)#, fix_imports=True)
|
with open(path, 'rb') as infile:
|
||||||
|
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'], time=event_dmt['datetime'], longitude=event_dmt['longitude'],
|
|
||||||
latitude=event_dmt['latitude'], depth=event_dmt['depth'])
|
origin = Origin(resource_id=event_dmt['origin_id'],
|
||||||
mag = Magnitude(mag=event_dmt['magnitude'], magnitude_type=event_dmt['magnitude_type'],
|
time=event_dmt['datetime'],
|
||||||
|
longitude=event_dmt['longitude'],
|
||||||
|
latitude=event_dmt['latitude'],
|
||||||
|
depth=event_dmt['depth'])
|
||||||
|
mag = Magnitude(mag=event_dmt['magnitude'],
|
||||||
|
magnitude_type=event_dmt['magnitude_type'],
|
||||||
origin_id=event_dmt['origin_id'])
|
origin_id=event_dmt['origin_id'])
|
||||||
|
|
||||||
ev.magnitudes.append(mag)
|
ev.magnitudes.append(mag)
|
||||||
ev.origins.append(origin)
|
ev.origins.append(origin)
|
||||||
return ev
|
return ev
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ Created on Wed Jan 26 17:47:25 2015
|
|||||||
@author: sebastianw
|
@author: sebastianw
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from pylot.core.io.data import SeiscompDataStructure, PilotDataStructure, ObspyDMTdataStructure
|
from pylot.core.io.data import SeiscompDataStructure, PilotDataStructure
|
||||||
|
|
||||||
DATASTRUCTURE = {'PILOT': PilotDataStructure, 'SeisComP': SeiscompDataStructure,
|
DATASTRUCTURE = {'PILOT': PilotDataStructure, 'SeisComP': SeiscompDataStructure,
|
||||||
'obspyDMT': ObspyDMTdataStructure, None: PilotDataStructure}
|
'obspyDMT': PilotDataStructure, None: PilotDataStructure}
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ import os
|
|||||||
import sys
|
import sys
|
||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
from PySide.QtCore import QThread, Signal, Qt, Slot, QRunnable, QObject
|
from PySide2.QtCore import QThread, Signal, Qt, Slot, QRunnable, QObject
|
||||||
from PySide.QtGui import QDialog, QProgressBar, QLabel, QHBoxLayout
|
from PySide2.QtWidgets import QDialog, QProgressBar, QLabel, QHBoxLayout, QPushButton
|
||||||
|
|
||||||
|
|
||||||
class Thread(QThread):
|
class Thread(QThread):
|
||||||
@@ -22,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:
|
||||||
@@ -39,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:
|
||||||
@@ -49,7 +52,23 @@ class Thread(QThread):
|
|||||||
# self.pb_widget.setWindowFlags(Qt.SplashScreen)
|
# self.pb_widget.setWindowFlags(Qt.SplashScreen)
|
||||||
# self.pb_widget.setModal(True)
|
# self.pb_widget.setModal(True)
|
||||||
|
|
||||||
self.pb_widget.label.setText(self.progressText)
|
# generate widget if not given in init
|
||||||
|
if not self.pb_widget:
|
||||||
|
self.pb_widget = QDialog(self.parent())
|
||||||
|
self.pb_widget.setWindowFlags(Qt.SplashScreen)
|
||||||
|
self.pb_widget.setModal(True)
|
||||||
|
|
||||||
|
# add button
|
||||||
|
delete_button = QPushButton('X')
|
||||||
|
delete_button.clicked.connect(self.exit)
|
||||||
|
hl = QHBoxLayout()
|
||||||
|
pb = QProgressBar()
|
||||||
|
pb.setRange(0, 0)
|
||||||
|
hl.addWidget(pb)
|
||||||
|
hl.addWidget(QLabel(self.progressText))
|
||||||
|
if self.abortButton:
|
||||||
|
hl.addWidget(delete_button)
|
||||||
|
self.pb_widget.setLayout(hl)
|
||||||
self.pb_widget.show()
|
self.pb_widget.show()
|
||||||
|
|
||||||
def hideProgressbar(self):
|
def hideProgressbar(self):
|
||||||
@@ -80,10 +99,12 @@ 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:
|
||||||
@@ -96,7 +117,7 @@ class Worker(QRunnable):
|
|||||||
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)
|
||||||
@@ -128,16 +149,18 @@ 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:
|
||||||
self.ncores = multiprocessing.cpu_count()
|
self.ncores = multiprocessing.cpu_count()
|
||||||
pool = multiprocessing.Pool(self.ncores)
|
pool = multiprocessing.Pool(self.ncores, maxtasksperchild=1000)
|
||||||
self.data = pool.map_async(self.func, self.args, callback=self.emitDone)
|
self.data = pool.map_async(self.func, self.args, callback=self.emitDone)
|
||||||
# self.data = pool.apply_async(self.func, self.shotlist, callback=self.emitDone) #emit each time returned
|
# self.data = pool.apply_async(self.func, self.shotlist, callback=self.emitDone) #emit each time returned
|
||||||
pool.close()
|
pool.close()
|
||||||
@@ -148,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:
|
||||||
|
|||||||
+273
-157
@@ -1,13 +1,16 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
import glob
|
||||||
import hashlib
|
import hashlib
|
||||||
import numpy as np
|
import logging
|
||||||
import os
|
import os
|
||||||
import platform
|
import platform
|
||||||
import re
|
import re
|
||||||
import subprocess
|
import subprocess
|
||||||
import warnings
|
import warnings
|
||||||
|
from functools import lru_cache
|
||||||
|
|
||||||
|
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
|
||||||
@@ -17,6 +20,10 @@ from pylot.core.io.inputs import PylotParameter, FilterOptions
|
|||||||
from pylot.core.util.obspyDMT_interface import check_obspydmt_eventfolder
|
from pylot.core.util.obspyDMT_interface import check_obspydmt_eventfolder
|
||||||
from pylot.styles import style_settings
|
from pylot.styles import style_settings
|
||||||
|
|
||||||
|
Rgba: Type[tuple] = Tuple[int, int, int, int]
|
||||||
|
Mplrgba: Type[tuple] = Tuple[float, float, float, float]
|
||||||
|
Mplrgbastr: Type[tuple] = Tuple[str, str, str, str]
|
||||||
|
|
||||||
|
|
||||||
def _pickle_method(m):
|
def _pickle_method(m):
|
||||||
if m.im_self is None:
|
if m.im_self is None:
|
||||||
@@ -36,15 +43,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)
|
||||||
|
|
||||||
|
|
||||||
@@ -81,25 +87,6 @@ def fit_curve(x, y):
|
|||||||
return splev, splrep(x, y)
|
return splev, splrep(x, y)
|
||||||
|
|
||||||
|
|
||||||
def getindexbounds(f, eta):
|
|
||||||
"""
|
|
||||||
Get indices of values closest below and above maximum value in an array
|
|
||||||
:param f: array
|
|
||||||
:type f: `~numpy.ndarray`
|
|
||||||
:param eta: look for value in array that is closes to max_value * eta
|
|
||||||
:type eta: float
|
|
||||||
:return: tuple containing index of max value, index of value closest below max value,
|
|
||||||
index of value closest above max value
|
|
||||||
:rtype: (int, int, int)
|
|
||||||
"""
|
|
||||||
mi = f.argmax() # get indices of max values
|
|
||||||
m = max(f) # get maximum value
|
|
||||||
b = m * eta #
|
|
||||||
l = find_nearest(f[:mi], b) # find closest value below max value
|
|
||||||
u = find_nearest(f[mi:], b) + mi # find closest value above max value
|
|
||||||
return mi, l, u
|
|
||||||
|
|
||||||
|
|
||||||
def gen_Pool(ncores=0):
|
def gen_Pool(ncores=0):
|
||||||
"""
|
"""
|
||||||
Generate mulitprocessing pool object utilizing ncores amount of cores
|
Generate mulitprocessing pool object utilizing ncores amount of cores
|
||||||
@@ -165,11 +152,11 @@ def clims(lim1, lim2):
|
|||||||
"""
|
"""
|
||||||
takes two pairs of limits and returns one pair of common limts
|
takes two pairs of limits and returns one pair of common limts
|
||||||
:param lim1: limit 1
|
:param lim1: limit 1
|
||||||
:type lim1: int
|
:type lim1: List[int]
|
||||||
:param lim2: limit 2
|
:param lim2: limit 2
|
||||||
:type lim2: int
|
:type lim2: List[int]
|
||||||
:return: new upper and lower limit common to both given limits
|
:return: new upper and lower limit common to both given limits
|
||||||
:rtype: [int, int]
|
:rtype: List[int]
|
||||||
|
|
||||||
>>> clims([0, 4], [1, 3])
|
>>> clims([0, 4], [1, 3])
|
||||||
[0, 4]
|
[0, 4]
|
||||||
@@ -227,7 +214,7 @@ def findComboBoxIndex(combo_box, val):
|
|||||||
:type val: basestring
|
:type val: basestring
|
||||||
:return: index value of item with name val or 0
|
:return: index value of item with name val or 0
|
||||||
"""
|
"""
|
||||||
return combo_box.findText(val) if combo_box.findText(val) is not -1 else 0
|
return combo_box.findText(val) if combo_box.findText(val) != -1 else 0
|
||||||
|
|
||||||
|
|
||||||
def find_in_list(list, str):
|
def find_in_list(list, str):
|
||||||
@@ -301,7 +288,7 @@ def fnConstructor(s):
|
|||||||
if type(s) is str:
|
if type(s) is str:
|
||||||
s = s.split(':')[-1]
|
s = s.split(':')[-1]
|
||||||
else:
|
else:
|
||||||
s = getHash(UTCDateTime())
|
s = get_hash(UTCDateTime())
|
||||||
|
|
||||||
badchars = re.compile(r'[^A-Za-z0-9_. ]+|^\.|\.$|^ | $|^$')
|
badchars = re.compile(r'[^A-Za-z0-9_. ]+|^\.|\.$|^ | $|^$')
|
||||||
badsuffix = re.compile(r'(aux|com[1-9]|con|lpt[1-9]|prn)(\.|$)')
|
badsuffix = re.compile(r'(aux|com[1-9]|con|lpt[1-9]|prn)(\.|$)')
|
||||||
@@ -313,32 +300,77 @@ def fnConstructor(s):
|
|||||||
return fn
|
return fn
|
||||||
|
|
||||||
|
|
||||||
def get_None(value):
|
def get_none(value):
|
||||||
"""
|
"""
|
||||||
Convert "None" to None
|
Convert "None" to None
|
||||||
:param value:
|
:param value:
|
||||||
:type value: str, bool
|
:type value: str, NoneType
|
||||||
:return:
|
:return:
|
||||||
:rtype: bool
|
:rtype: type(value) or NoneType
|
||||||
|
|
||||||
|
>>> st = read()
|
||||||
|
>>> print(get_none(st))
|
||||||
|
3 Trace(s) in Stream:
|
||||||
|
BW.RJOB..EHZ | 2009-08-24T00:20:03.000000Z - 2009-08-24T00:20:32.990000Z | 100.0 Hz, 3000 samples
|
||||||
|
BW.RJOB..EHN | 2009-08-24T00:20:03.000000Z - 2009-08-24T00:20:32.990000Z | 100.0 Hz, 3000 samples
|
||||||
|
BW.RJOB..EHE | 2009-08-24T00:20:03.000000Z - 2009-08-24T00:20:32.990000Z | 100.0 Hz, 3000 samples
|
||||||
|
>>> get_none('Stream')
|
||||||
|
'Stream'
|
||||||
|
>>> get_none(0)
|
||||||
|
0
|
||||||
|
>>> get_none(0.)
|
||||||
|
0.0
|
||||||
|
>>> print(get_none('None'))
|
||||||
|
None
|
||||||
|
>>> print(get_none(None))
|
||||||
|
None
|
||||||
"""
|
"""
|
||||||
if value == 'None':
|
if value is None or (type(value) is str and value == 'None'):
|
||||||
return None
|
return None
|
||||||
else:
|
else:
|
||||||
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. Return value if it cannot be identified as bool.
|
||||||
:param value:
|
:param value:
|
||||||
:type value: str, bool
|
:type value: str, bool, int, float
|
||||||
:return: true boolean value
|
:return: true boolean value
|
||||||
:rtype: bool
|
:rtype: bool
|
||||||
|
|
||||||
|
>>> get_bool(True)
|
||||||
|
True
|
||||||
|
>>> get_bool(False)
|
||||||
|
False
|
||||||
|
>>> get_bool(0)
|
||||||
|
False
|
||||||
|
>>> get_bool(0.)
|
||||||
|
False
|
||||||
|
>>> get_bool(0.1)
|
||||||
|
True
|
||||||
|
>>> get_bool(2)
|
||||||
|
True
|
||||||
|
>>> get_bool(-1)
|
||||||
|
False
|
||||||
|
>>> get_bool(-0.3)
|
||||||
|
False
|
||||||
|
>>> get_bool(None)
|
||||||
|
None
|
||||||
|
>>> get_bool('Stream')
|
||||||
|
'Stream'
|
||||||
"""
|
"""
|
||||||
if value in ['True', 'true']:
|
if type(value) == 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
|
||||||
|
elif isinstance(value, float) or isinstance(value, int):
|
||||||
|
if value > 0. or value > 0:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
else:
|
else:
|
||||||
return value
|
return value
|
||||||
|
|
||||||
@@ -352,8 +384,8 @@ def four_digits(year):
|
|||||||
:return: four digit year correspondent
|
:return: four digit year correspondent
|
||||||
:rtype: int
|
:rtype: int
|
||||||
|
|
||||||
>>> four_digits(20)
|
>>> four_digits(75)
|
||||||
1920
|
1975
|
||||||
>>> four_digits(16)
|
>>> four_digits(16)
|
||||||
2016
|
2016
|
||||||
>>> four_digits(00)
|
>>> four_digits(00)
|
||||||
@@ -435,36 +467,53 @@ def backtransformFilterString(st):
|
|||||||
return st
|
return st
|
||||||
|
|
||||||
|
|
||||||
def getHash(time):
|
def get_hash(time):
|
||||||
"""
|
"""
|
||||||
takes a time object and returns the corresponding SHA1 hash of the formatted date string
|
takes a time object and returns the corresponding SHA1 hash of the formatted date string
|
||||||
:param time: time object for which a hash should be calculated
|
:param time: time object for which a hash should be calculated
|
||||||
:type time: `~obspy.core.utcdatetime.UTCDateTime`
|
:type time: `~obspy.core.utcdatetime.UTCDateTime`
|
||||||
:return: SHA1 hash
|
:return: SHA1 hash
|
||||||
:rtype: str
|
:rtype: str
|
||||||
|
|
||||||
|
>>> time = UTCDateTime(0)
|
||||||
|
>>> get_hash(time)
|
||||||
|
'7627cce3b1b58dd21b005dac008b34d18317dd15'
|
||||||
|
>>> get_hash(0)
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
AssertionError: 'time' is not an ObsPy UTCDateTime object
|
||||||
"""
|
"""
|
||||||
|
assert isinstance(time, UTCDateTime), '\'time\' is not an ObsPy UTCDateTime object'
|
||||||
hg = hashlib.sha1()
|
hg = hashlib.sha1()
|
||||||
hg.update(time.strftime('%Y-%m-%d %H:%M:%S.%f'))
|
hg.update(time.strftime('%Y-%m-%d %H:%M:%S.%f').encode('utf-8'))
|
||||||
return hg.hexdigest()
|
return hg.hexdigest()
|
||||||
|
|
||||||
|
|
||||||
def getLogin():
|
def get_login():
|
||||||
"""
|
"""
|
||||||
returns the actual user's login ID
|
returns the actual user's name
|
||||||
:return: login ID
|
:return: login name
|
||||||
:rtype: str
|
:rtype: str
|
||||||
"""
|
"""
|
||||||
import getpass
|
import getpass
|
||||||
return getpass.getuser()
|
return getpass.getuser()
|
||||||
|
|
||||||
|
|
||||||
def getOwner(fn):
|
def get_owner(fn):
|
||||||
"""
|
"""
|
||||||
takes a filename and return the login ID of the actual owner of the file
|
takes a filename and return the login ID of the actual owner of the file
|
||||||
:param fn: filename of the file tested
|
:param fn: filename of the file tested
|
||||||
:type fn: str
|
:type fn: str
|
||||||
:return: login ID of the file's owner
|
:return: login ID of the file's owner
|
||||||
:rtype: str
|
:rtype: str
|
||||||
|
|
||||||
|
>>> import tempfile
|
||||||
|
>>> with tempfile.NamedTemporaryFile() as tmpfile:
|
||||||
|
... tmpfile.write(b'') and True
|
||||||
|
... tmpfile.flush()
|
||||||
|
... get_owner(tmpfile.name) == os.path.expanduser('~').split('/')[-1]
|
||||||
|
0
|
||||||
|
True
|
||||||
"""
|
"""
|
||||||
system_name = platform.system()
|
system_name = platform.system()
|
||||||
if system_name in ["Linux", "Darwin"]:
|
if system_name in ["Linux", "Darwin"]:
|
||||||
@@ -510,6 +559,11 @@ def is_executable(fn):
|
|||||||
:param fn: path to the file to be tested
|
:param fn: path to the file to be tested
|
||||||
:return: True or False
|
:return: True or False
|
||||||
:rtype: bool
|
:rtype: bool
|
||||||
|
|
||||||
|
>>> is_executable('/bin/ls')
|
||||||
|
True
|
||||||
|
>>> is_executable('/var/log/system.log')
|
||||||
|
False
|
||||||
"""
|
"""
|
||||||
return os.path.isfile(fn) and os.access(fn, os.X_OK)
|
return os.path.isfile(fn) and os.access(fn, os.X_OK)
|
||||||
|
|
||||||
@@ -536,24 +590,36 @@ def isSorted(iterable):
|
|||||||
>>> isSorted([2,3,1,4])
|
>>> isSorted([2,3,1,4])
|
||||||
False
|
False
|
||||||
"""
|
"""
|
||||||
assert isIterable(iterable), 'object is not iterable; object: {' \
|
assert is_iterable(iterable), "object is not iterable; object: {}".format(iterable)
|
||||||
'}'.format(iterable)
|
|
||||||
if type(iterable) is str:
|
if type(iterable) is str:
|
||||||
iterable = [s for s in iterable]
|
iterable = [s for s in iterable]
|
||||||
return sorted(iterable) == iterable
|
return sorted(iterable) == iterable
|
||||||
|
|
||||||
|
|
||||||
def isIterable(obj):
|
def is_iterable(obj):
|
||||||
"""
|
"""
|
||||||
takes a python object and returns True is the object is iterable and
|
takes a python object and returns True is the object is iterable and
|
||||||
False otherwise
|
False otherwise
|
||||||
:param obj: a python object
|
:param obj: a python object
|
||||||
:type obj: object
|
:type obj: obj
|
||||||
:return: True of False
|
:return: True of False
|
||||||
:rtype: bool
|
:rtype: bool
|
||||||
|
|
||||||
|
>>> is_iterable(1)
|
||||||
|
False
|
||||||
|
>>> is_iterable(True)
|
||||||
|
False
|
||||||
|
>>> is_iterable(0.)
|
||||||
|
False
|
||||||
|
>>> is_iterable((0,1,3,4))
|
||||||
|
True
|
||||||
|
>>> is_iterable([1])
|
||||||
|
True
|
||||||
|
>>> is_iterable('a')
|
||||||
|
True
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
iterator = iter(obj)
|
iter(obj)
|
||||||
except TypeError as te:
|
except TypeError as te:
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
@@ -562,13 +628,19 @@ def isIterable(obj):
|
|||||||
def key_for_set_value(d):
|
def key_for_set_value(d):
|
||||||
"""
|
"""
|
||||||
takes a dictionary and returns the first key for which's value the
|
takes a dictionary and returns the first key for which's value the
|
||||||
boolean is True
|
boolean representation is True
|
||||||
:param d: dictionary containing values
|
:param d: dictionary containing values
|
||||||
:type d: dict
|
:type d: dict
|
||||||
:return: key to the first non-False value found; None if no value's
|
:return: key to the first non-False value found; None if no value's
|
||||||
boolean equals True
|
boolean equals True
|
||||||
:rtype:
|
:rtype: bool or NoneType
|
||||||
|
|
||||||
|
>>> key_for_set_value({'one': 0, 'two': 1})
|
||||||
|
'two'
|
||||||
|
>>> print(key_for_set_value({1: 0, 2: False}))
|
||||||
|
None
|
||||||
"""
|
"""
|
||||||
|
assert type(d) is dict, "Function only defined for inputs of type 'dict'."
|
||||||
r = None
|
r = None
|
||||||
for k, v in d.items():
|
for k, v in d.items():
|
||||||
if v:
|
if v:
|
||||||
@@ -576,32 +648,53 @@ def key_for_set_value(d):
|
|||||||
return r
|
return r
|
||||||
|
|
||||||
|
|
||||||
def prepTimeAxis(stime, trace, verbosity=0):
|
def prep_time_axis(offset, trace, verbosity=0):
|
||||||
"""
|
"""
|
||||||
takes a starttime and a trace object and returns a valid time axis for
|
takes an offset and a trace object and returns a valid time axis for
|
||||||
plotting
|
plotting
|
||||||
:param stime: start time of the actual seismogram as UTCDateTime
|
:param offset: offset of the actual seismogram on plotting axis
|
||||||
:type stime: `~obspy.core.utcdatetime.UTCDateTime`
|
:type offset: float or int
|
||||||
:param trace: seismic trace object
|
:param trace: seismic trace object
|
||||||
:type trace: `~obspy.core.trace.Trace`
|
:type trace: `~obspy.core.trace.Trace`
|
||||||
:param verbosity: if != 0, debug output will be written to console
|
:param verbosity: if != 0, debug output will be written to console
|
||||||
:type verbosity: int
|
:type verbosity: int
|
||||||
:return: valid numpy array with time stamps for plotting
|
:return: valid numpy array with time stamps for plotting
|
||||||
:rtype: `~numpy.ndarray`
|
:rtype: `~numpy.ndarray`
|
||||||
|
|
||||||
|
>>> tr = read()[0]
|
||||||
|
>>> prep_time_axis(0., tr)
|
||||||
|
array([0.00000000e+00, 1.00033344e-02, 2.00066689e-02, ...,
|
||||||
|
2.99799933e+01, 2.99899967e+01, 3.00000000e+01])
|
||||||
|
>>> prep_time_axis(22.5, tr)
|
||||||
|
array([22.5 , 22.51000333, 22.52000667, ..., 52.47999333,
|
||||||
|
52.48999667, 52.5 ])
|
||||||
|
>>> prep_time_axis(tr.stats.starttime, tr)
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
AssertionError: 'offset' is not of type 'float' or 'int'; type: <class 'obspy.core.utcdatetime.UTCDateTime'>
|
||||||
|
>>> tr.stats.npts -= 1
|
||||||
|
>>> prep_time_axis(0, tr)
|
||||||
|
array([0.00000000e+00, 1.00033356e-02, 2.00066711e-02, ...,
|
||||||
|
2.99699933e+01, 2.99799967e+01, 2.99900000e+01])
|
||||||
|
>>> tr.stats.npts += 2
|
||||||
|
>>> prep_time_axis(0, tr)
|
||||||
|
array([0.00000000e+00, 1.00033333e-02, 2.00066667e-02, ...,
|
||||||
|
2.99899933e+01, 2.99999967e+01, 3.00100000e+01])
|
||||||
"""
|
"""
|
||||||
|
assert isinstance(offset, (float, int)), "'offset' is not of type 'float' or 'int'; type: {}".format(type(offset))
|
||||||
nsamp = trace.stats.npts
|
nsamp = trace.stats.npts
|
||||||
srate = trace.stats.sampling_rate
|
srate = trace.stats.sampling_rate
|
||||||
tincr = trace.stats.delta
|
tincr = trace.stats.delta
|
||||||
etime = stime + nsamp / srate
|
etime = offset + nsamp / srate
|
||||||
time_ax = np.linspace(stime, etime, nsamp)
|
time_ax = np.linspace(offset, etime, nsamp)
|
||||||
if len(time_ax) < nsamp:
|
if len(time_ax) < nsamp:
|
||||||
if verbosity:
|
if verbosity:
|
||||||
print('elongate time axes by one datum')
|
print('elongate time axes by one datum')
|
||||||
time_ax = np.arange(stime, etime + tincr, tincr)
|
time_ax = np.arange(offset, etime + tincr, tincr)
|
||||||
elif len(time_ax) > nsamp:
|
elif len(time_ax) > nsamp:
|
||||||
if verbosity:
|
if verbosity:
|
||||||
print('shorten time axes by one datum')
|
print('shorten time axes by one datum')
|
||||||
time_ax = np.arange(stime, etime - tincr, tincr)
|
time_ax = np.arange(offset, etime - tincr, tincr)
|
||||||
if len(time_ax) != nsamp:
|
if len(time_ax) != nsamp:
|
||||||
print('Station {0}, {1} samples of data \n '
|
print('Station {0}, {1} samples of data \n '
|
||||||
'{2} length of time vector \n'
|
'{2} length of time vector \n'
|
||||||
@@ -617,13 +710,13 @@ def find_horizontals(data):
|
|||||||
:param data: waveform data
|
:param data: waveform data
|
||||||
:type data: `obspy.core.stream.Stream`
|
:type data: `obspy.core.stream.Stream`
|
||||||
:return: components list
|
:return: components list
|
||||||
:rtype: list
|
:rtype: List(str)
|
||||||
|
|
||||||
..example::
|
..example::
|
||||||
|
|
||||||
>>> st = read()
|
>>> st = read()
|
||||||
>>> find_horizontals(st)
|
>>> find_horizontals(st)
|
||||||
[u'N', u'E']
|
['N', 'E']
|
||||||
"""
|
"""
|
||||||
rval = []
|
rval = []
|
||||||
for tr in data:
|
for tr in data:
|
||||||
@@ -634,7 +727,7 @@ def find_horizontals(data):
|
|||||||
return rval
|
return rval
|
||||||
|
|
||||||
|
|
||||||
def pick_color(picktype, phase, quality=0):
|
def pick_color(picktype: Literal['manual', 'automatic'], phase: Literal['P', 'S'], quality: int = 0) -> Rgba:
|
||||||
"""
|
"""
|
||||||
Create pick color by modifying the base color by the quality.
|
Create pick color by modifying the base color by the quality.
|
||||||
|
|
||||||
@@ -647,7 +740,7 @@ def pick_color(picktype, phase, quality=0):
|
|||||||
:param quality: quality of pick. Decides the new intensity of the modifier color
|
:param quality: quality of pick. Decides the new intensity of the modifier color
|
||||||
:type quality: int
|
:type quality: int
|
||||||
:return: tuple containing modified rgba color values
|
:return: tuple containing modified rgba color values
|
||||||
:rtype: (int, int, int, int)
|
:rtype: Rgba
|
||||||
"""
|
"""
|
||||||
min_quality = 3
|
min_quality = 3
|
||||||
bpc = base_phase_colors(picktype, phase) # returns dict like {'modifier': 'g', 'rgba': (0, 0, 255, 255)}
|
bpc = base_phase_colors(picktype, phase) # returns dict like {'modifier': 'g', 'rgba': (0, 0, 255, 255)}
|
||||||
@@ -703,17 +796,17 @@ def pick_linestyle_plt(picktype, key):
|
|||||||
return linestyles[picktype][key]
|
return linestyles[picktype][key]
|
||||||
|
|
||||||
|
|
||||||
def modify_rgba(rgba, modifier, intensity):
|
def modify_rgba(rgba: Rgba, modifier: Literal['r', 'g', 'b'], intensity: float) -> Rgba:
|
||||||
"""
|
"""
|
||||||
Modify rgba color by adding the given intensity to the modifier color
|
Modify rgba color by adding the given intensity to the modifier color
|
||||||
:param rgba: tuple containing rgba values
|
:param rgba: tuple containing rgba values
|
||||||
:type rgba: (int, int, int, int)
|
:type rgba: Rgba
|
||||||
:param modifier: which color should be modified, eg. 'r', 'g', 'b'
|
:param modifier: which color should be modified; options: 'r', 'g', 'b'
|
||||||
:type modifier: str
|
:type modifier: Literal['r', 'g', 'b']
|
||||||
:param intensity: intensity to be added to selected color
|
:param intensity: intensity to be added to selected color
|
||||||
:type intensity: float
|
:type intensity: float
|
||||||
:return: tuple containing rgba values
|
:return: tuple containing rgba values
|
||||||
:rtype: (int, int, int, int)
|
:rtype: Rgba
|
||||||
"""
|
"""
|
||||||
rgba = list(rgba)
|
rgba = list(rgba)
|
||||||
index = {'r': 0,
|
index = {'r': 0,
|
||||||
@@ -747,18 +840,20 @@ def transform_colors_mpl_str(colors, no_alpha=False):
|
|||||||
Transforms rgba color values to a matplotlib string of color values with a range of [0, 1]
|
Transforms rgba color values to a matplotlib string of color values with a range of [0, 1]
|
||||||
:param colors: tuple of rgba color values ranging from [0, 255]
|
:param colors: tuple of rgba color values ranging from [0, 255]
|
||||||
:type colors: (float, float, float, float)
|
:type colors: (float, float, float, float)
|
||||||
:param no_alpha: Wether to return a alpha value in the matplotlib color string
|
:param no_alpha: Whether to return an alpha value in the matplotlib color string
|
||||||
:type no_alpha: bool
|
:type no_alpha: bool
|
||||||
:return: String containing r, g, b values and alpha value if no_alpha is False (default)
|
:return: String containing r, g, b values and alpha value if no_alpha is False (default)
|
||||||
:rtype: str
|
:rtype: str
|
||||||
|
|
||||||
|
>>> transform_colors_mpl_str((255., 255., 255., 255.), True)
|
||||||
|
'(1.0, 1.0, 1.0)'
|
||||||
|
>>> transform_colors_mpl_str((255., 255., 255., 255.))
|
||||||
|
'(1.0, 1.0, 1.0, 1.0)'
|
||||||
"""
|
"""
|
||||||
colors = list(colors)
|
|
||||||
colors_mpl = tuple([color / 255. for color in colors])
|
|
||||||
if no_alpha:
|
if no_alpha:
|
||||||
colors_mpl = '({}, {}, {})'.format(*colors_mpl)
|
return '({}, {}, {})'.format(*transform_colors_mpl(colors))
|
||||||
else:
|
else:
|
||||||
colors_mpl = '({}, {}, {}, {})'.format(*colors_mpl)
|
return '({}, {}, {}, {})'.format(*transform_colors_mpl(colors))
|
||||||
return colors_mpl
|
|
||||||
|
|
||||||
|
|
||||||
def transform_colors_mpl(colors):
|
def transform_colors_mpl(colors):
|
||||||
@@ -768,27 +863,16 @@ def transform_colors_mpl(colors):
|
|||||||
:type colors: (float, float, float, float)
|
:type colors: (float, float, float, float)
|
||||||
:return: tuple of rgba color values ranging from [0, 1]
|
:return: tuple of rgba color values ranging from [0, 1]
|
||||||
:rtype: (float, float, float, float)
|
:rtype: (float, float, float, float)
|
||||||
|
|
||||||
|
>>> transform_colors_mpl((127.5, 0., 63.75, 255.))
|
||||||
|
(0.5, 0.0, 0.25, 1.0)
|
||||||
|
>>> transform_colors_mpl(())
|
||||||
"""
|
"""
|
||||||
colors = list(colors)
|
colors = list(colors)
|
||||||
colors_mpl = tuple([color / 255. for color in colors])
|
colors_mpl = tuple([color / 255. for color in colors])
|
||||||
return colors_mpl
|
return colors_mpl
|
||||||
|
|
||||||
|
|
||||||
def remove_underscores(data):
|
|
||||||
"""
|
|
||||||
takes a `obspy.core.stream.Stream` object and removes all underscores
|
|
||||||
from station names
|
|
||||||
:param data: stream of seismic data
|
|
||||||
:type data: `~obspy.core.stream.Stream`
|
|
||||||
:return: data stream
|
|
||||||
:rtype: `~obspy.core.stream.Stream`
|
|
||||||
"""
|
|
||||||
# for tr in data:
|
|
||||||
# # remove underscores
|
|
||||||
# tr.stats.station = tr.stats.station.strip('_')
|
|
||||||
return data
|
|
||||||
|
|
||||||
|
|
||||||
def trim_station_components(data, trim_start=True, trim_end=True):
|
def trim_station_components(data, trim_start=True, trim_end=True):
|
||||||
"""
|
"""
|
||||||
cut a stream so only the part common to all three traces is kept to avoid dealing with offsets
|
cut a stream so only the part common to all three traces is kept to avoid dealing with offsets
|
||||||
@@ -896,13 +980,53 @@ def check4doubled(data):
|
|||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
def check_for_nan(data, nan_value=0.):
|
||||||
|
"""
|
||||||
|
Replace all NaNs in data with nan_value (in place)
|
||||||
|
:param data: stream of seismic data
|
||||||
|
:type data: `~obspy.core.stream.Stream`
|
||||||
|
:param nan_value: value which all NaNs are set to
|
||||||
|
:type nan_value: float, int
|
||||||
|
:return: None
|
||||||
|
"""
|
||||||
|
if not data:
|
||||||
|
return
|
||||||
|
for trace in data:
|
||||||
|
np.nan_to_num(trace.data, copy=False, nan=nan_value)
|
||||||
|
|
||||||
|
|
||||||
|
def get_pylot_eventfile_with_extension(event, fext):
|
||||||
|
if hasattr(event, 'path'):
|
||||||
|
eventpath = event.path
|
||||||
|
else:
|
||||||
|
logging.warning('No attribute path found for event.')
|
||||||
|
return
|
||||||
|
eventname = event.pylot_id #path.split('/')[-1] # or event.pylot_id
|
||||||
|
filename = os.path.join(eventpath, 'PyLoT_' + eventname + fext)
|
||||||
|
if os.path.isfile(filename):
|
||||||
|
return filename
|
||||||
|
|
||||||
|
|
||||||
|
def get_possible_pylot_eventfile_extensions(event, fext):
|
||||||
|
if hasattr(event, 'path'):
|
||||||
|
eventpath = event.path
|
||||||
|
else:
|
||||||
|
logging.warning('No attribute path found for event.')
|
||||||
|
return []
|
||||||
|
eventname = event.pylot_id
|
||||||
|
filename = os.path.join(eventpath, 'PyLoT_' + eventname + fext)
|
||||||
|
filenames = glob.glob(filename)
|
||||||
|
extensions = [os.path.split(path)[-1].split('PyLoT_' + eventname)[-1] for path in filenames]
|
||||||
|
return extensions
|
||||||
|
|
||||||
|
|
||||||
def get_stations(data):
|
def get_stations(data):
|
||||||
"""
|
"""
|
||||||
Get list of all station names in data stream
|
Get list of all station names in data-stream
|
||||||
:param data: stream containing seismic traces
|
:param data: stream containing seismic traces
|
||||||
:type data: `~obspy.core.stream.Stream`
|
:type data: `~obspy.core.stream.Stream`
|
||||||
:return: list of all station names in data, no duplicates
|
:return: list of all station names in data, no duplicates
|
||||||
:rtype: list of str
|
:rtype: List(str)
|
||||||
"""
|
"""
|
||||||
stations = []
|
stations = []
|
||||||
for tr in data:
|
for tr in data:
|
||||||
@@ -929,63 +1053,87 @@ def check4rotated(data, metadata=None, verbosity=1):
|
|||||||
:rtype: `~obspy.core.stream.Stream`
|
:rtype: `~obspy.core.stream.Stream`
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def rotate_components(wfstream, metadata=None):
|
def rotation_required(trace_ids):
|
||||||
|
"""
|
||||||
|
Derive if any rotation is required from the orientation code of the input.
|
||||||
|
|
||||||
|
:param trace_ids: string identifier of waveform data trace
|
||||||
|
:type trace_ids: List(str)
|
||||||
|
:return: boolean representing if rotation is necessary for any of the traces
|
||||||
|
:rtype: bool
|
||||||
|
"""
|
||||||
|
orientations = [trace_id[-1] for trace_id in trace_ids]
|
||||||
|
return any([orientation.isnumeric() for orientation in orientations])
|
||||||
|
|
||||||
|
def rotate_components(wfs_in, metadata=None):
|
||||||
"""
|
"""
|
||||||
Rotate components if orientation code is numeric (= non traditional orientation).
|
Rotate components if orientation code is numeric (= non traditional orientation).
|
||||||
|
|
||||||
Azimut and dip are fetched from metadata. To be rotated, traces of a station have to be cut to the same length.
|
Azimut and dip are fetched from metadata. To be rotated, traces of a station have to be cut to the same length.
|
||||||
Returns unrotated traces of no metadata is provided
|
Returns unrotated traces of no metadata is provided
|
||||||
:param wfstream: stream containing seismic traces of a station
|
:param wfs_in: stream containing seismic traces of a station
|
||||||
:type wfstream: `~obspy.core.stream.Stream`
|
:type wfs_in: `~obspy.core.stream.Stream`
|
||||||
:param metadata: tuple containing metadata type string and metadata parser object
|
:param metadata: tuple containing metadata type string and metadata parser object
|
||||||
:type metadata: (str, `~obspy.io.xseed.parser.Parser`)
|
:type metadata: (str, `~obspy.io.xseed.parser.Parser`)
|
||||||
:return: stream object with traditionally oriented traces (ZNE)
|
:return: stream object with traditionally oriented traces (ZNE)
|
||||||
:rtype: `~obspy.core.stream.Stream`
|
:rtype: `~obspy.core.stream.Stream`
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
if len(wfs_in) < 3:
|
||||||
|
print(f"Stream {wfs_in=}, has not enough components to rotate.")
|
||||||
|
return wfs_in
|
||||||
|
|
||||||
# check if any traces in this station need to be rotated
|
# check if any traces in this station need to be rotated
|
||||||
trace_ids = [trace.id for trace in wfstream]
|
trace_ids = [trace.id for trace in wfs_in]
|
||||||
orientations = [trace_id[-1] for trace_id in trace_ids]
|
if not rotation_required(trace_ids):
|
||||||
rotation_required = [orientation.isnumeric() for orientation in orientations]
|
logging.debug(f"Stream does not need any rotation: Traces are {trace_ids=}")
|
||||||
if any(rotation_required):
|
return wfs_in
|
||||||
t_start = full_range(wfstream)
|
|
||||||
|
# check metadata quality
|
||||||
|
t_start = full_range(wfs_in)
|
||||||
try:
|
try:
|
||||||
azimuts = [metadata.get_coordinates(tr_id, t_start)['azimuth'] for tr_id in trace_ids]
|
azimuths = []
|
||||||
dips = [metadata.get_coordinates(tr_id, t_start)['dip'] for tr_id in trace_ids]
|
dips = []
|
||||||
except (KeyError, TypeError) as e:
|
for tr_id in trace_ids:
|
||||||
print('Failed to rotate trace {}, no azimuth or dip available in metadata'.format(trace_id))
|
azimuths.append(metadata.get_coordinates(tr_id, t_start)['azimuth'])
|
||||||
return wfstream
|
dips.append(metadata.get_coordinates(tr_id, t_start)['dip'])
|
||||||
if len(wfstream) < 3:
|
except (KeyError, TypeError) as err:
|
||||||
print('Failed to rotate Stream {}, not enough components available.'.format(wfstream))
|
logging.error(f"{type(err)=} occurred: {err=} Rotating not possible, not all azimuth and dip information "
|
||||||
return wfstream
|
f"available in metadata. Stream remains unchanged.")
|
||||||
|
return wfs_in
|
||||||
|
except Exception as err:
|
||||||
|
print(f"Unexpected {err=}, {type(err)=}")
|
||||||
|
raise
|
||||||
|
|
||||||
# to rotate all traces must have same length, so trim them
|
# to rotate all traces must have same length, so trim them
|
||||||
wfstream = trim_station_components(wfstream, trim_start=True, trim_end=True)
|
wfs_out = trim_station_components(wfs_in, trim_start=True, trim_end=True)
|
||||||
try:
|
try:
|
||||||
z, n, e = rotate2zne(wfstream[0], azimuts[0], dips[0],
|
z, n, e = rotate2zne(wfs_out[0], azimuths[0], dips[0],
|
||||||
wfstream[1], azimuts[1], dips[1],
|
wfs_out[1], azimuths[1], dips[1],
|
||||||
wfstream[2], azimuts[2], dips[2])
|
wfs_out[2], azimuths[2], dips[2])
|
||||||
print('check4rotated: rotated trace {} to ZNE'.format(trace_id))
|
print('check4rotated: rotated trace {} to ZNE'.format(trace_ids))
|
||||||
# replace old data with rotated data, change the channel code to ZNE
|
# replace old data with rotated data, change the channel code to ZNE
|
||||||
z_index = dips.index(min(
|
z_index = dips.index(min(
|
||||||
dips)) # get z-trace index, z has minimum dip of -90 (dip is measured from 0 to -90, with -90 being vertical)
|
dips)) # get z-trace index, z has minimum dip of -90 (dip is measured from 0 to -90, with -90
|
||||||
wfstream[z_index].data = z
|
# being vertical)
|
||||||
wfstream[z_index].stats.channel = wfstream[z_index].stats.channel[0:-1] + 'Z'
|
wfs_out[z_index].data = z
|
||||||
|
wfs_out[z_index].stats.channel = wfs_out[z_index].stats.channel[0:-1] + 'Z'
|
||||||
del trace_ids[z_index]
|
del trace_ids[z_index]
|
||||||
for trace_id in trace_ids:
|
for trace_id in trace_ids:
|
||||||
coordinates = metadata.get_coordinates(trace_id, t_start)
|
coordinates = metadata.get_coordinates(trace_id, t_start)
|
||||||
dip, az = coordinates['dip'], coordinates['azimuth']
|
dip, az = coordinates['dip'], coordinates['azimuth']
|
||||||
trace = wfstream.select(id=trace_id)[0]
|
trace = wfs_out.select(id=trace_id)[0]
|
||||||
if az > 315 or az <= 45 or az > 135 and az <= 225:
|
if az > 315 or az <= 45 or 135 < az <= 225:
|
||||||
trace.data = n
|
trace.data = n
|
||||||
trace.stats.channel = trace.stats.channel[0:-1] + 'N'
|
trace.stats.channel = trace.stats.channel[0:-1] + 'N'
|
||||||
elif az > 45 and az <= 135 or az > 225 and az <= 315:
|
elif 45 < az <= 135 or 225 < az <= 315:
|
||||||
trace.data = e
|
trace.data = e
|
||||||
trace.stats.channel = trace.stats.channel[0:-1] + 'E'
|
trace.stats.channel = trace.stats.channel[0:-1] + 'E'
|
||||||
except (ValueError) as e:
|
except ValueError as err:
|
||||||
print(e)
|
print(f"{err=} Rotation failed. Stream remains unchanged.")
|
||||||
return wfstream
|
return wfs_in
|
||||||
|
|
||||||
return wfstream
|
return wfs_out
|
||||||
|
|
||||||
if metadata is None:
|
if metadata is None:
|
||||||
if verbosity:
|
if verbosity:
|
||||||
@@ -999,38 +1147,6 @@ def check4rotated(data, metadata=None, verbosity=1):
|
|||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
def scaleWFData(data, factor=None, components='all'):
|
|
||||||
"""
|
|
||||||
produce scaled waveforms from given waveform data and a scaling factor,
|
|
||||||
waveform may be selected by their components name
|
|
||||||
:param data: waveform data to be scaled
|
|
||||||
:type data: `~obspy.core.stream.Stream` object
|
|
||||||
:param factor: scaling factor
|
|
||||||
:type factor: float
|
|
||||||
:param components: components labels for the traces in data to be scaled by
|
|
||||||
the scaling factor (optional, default: 'all')
|
|
||||||
:type components: tuple
|
|
||||||
:return: scaled waveform data
|
|
||||||
:rtype: `~obspy.core.stream.Stream` object
|
|
||||||
"""
|
|
||||||
if components is not 'all':
|
|
||||||
for comp in components:
|
|
||||||
if factor is None:
|
|
||||||
max_val = np.max(np.abs(data.select(component=comp)[0].data))
|
|
||||||
data.select(component=comp)[0].data /= 2 * max_val
|
|
||||||
else:
|
|
||||||
data.select(component=comp)[0].data /= 2 * factor
|
|
||||||
else:
|
|
||||||
for tr in data:
|
|
||||||
if factor is None:
|
|
||||||
max_val = float(np.max(np.abs(tr.data)))
|
|
||||||
tr.data /= 2 * max_val
|
|
||||||
else:
|
|
||||||
tr.data /= 2 * factor
|
|
||||||
|
|
||||||
return data
|
|
||||||
|
|
||||||
|
|
||||||
def runProgram(cmd, parameter=None):
|
def runProgram(cmd, parameter=None):
|
||||||
"""
|
"""
|
||||||
run an external program specified by cmd with parameters input returning the
|
run an external program specified by cmd with parameters input returning the
|
||||||
@@ -1165,7 +1281,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
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
+955
-415
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,9 @@
|
|||||||
|
# This file may be used to create an environment using:
|
||||||
|
# $ conda create --name <env> --file <this file>
|
||||||
|
# platform: win-64
|
||||||
|
cartopy>=0.20.2
|
||||||
|
numpy<2
|
||||||
|
obspy>=1.3.0
|
||||||
|
pyqtgraph>=0.12.4
|
||||||
|
pyside2>=5.13.2
|
||||||
|
scipy>=1.8.0
|
||||||
@@ -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', 'PySide', 'matplotlib', 'numpy', 'scipy', 'pyqtgraph'],
|
|
||||||
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
@@ -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")
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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,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()
|
||||||
|
|||||||
@@ -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()
|
||||||
@@ -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()
|
||||||
|
|||||||
@@ -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):
|
||||||
|
|||||||
Reference in New Issue
Block a user