Compare commits
31
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 |
@@ -2,4 +2,3 @@
|
||||
*~
|
||||
.idea
|
||||
pylot/RELEASE-VERSION
|
||||
/tests/test_autopicker/dmt_database_test/
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
Darius Arnold <Darius.Arnold@ruhr-uni-bochum.de> <Darius_A@web.de>
|
||||
Darius Arnold <Darius.Arnold@ruhr-uni-bochum.de> <darius.arnold@rub.de>
|
||||
Darius Arnold <Darius.Arnold@ruhr-uni-bochum.de> <darius.arnold@ruhr-uni-bochum.de>
|
||||
Darius Arnold <Darius.Arnold@ruhr-uni-bochum.de> <mail@dariusarnold.de>
|
||||
|
||||
Dennis Wlecklik <dennisw@minos02.geophysik.ruhr-uni-bochum.de>
|
||||
|
||||
Jeldrik Gaal <jeldrikgaal@gmail.com>
|
||||
|
||||
Kaan Coekerim <kaan.coekerim@ruhr-uni-bochum.de>
|
||||
Kaan Coekerim <kaan.coekerim@ruhr-uni-bochum.de> <kaan.coekerim@rub.de>
|
||||
|
||||
Ludger Kueperkoch <kueperkoch@igem-energie.de> <kueperkoch@bestec-for-nature.com>
|
||||
Ludger Kueperkoch <kueperkoch@igem-energie.de> <ludger@quake2.(none)>
|
||||
Ludger Kueperkoch <kueperkoch@igem-energie.de> <ludger@sauron.bestec-for-nature>
|
||||
|
||||
Marc S. Boxberg <marc.boxberg@rub.de>
|
||||
|
||||
Marcel Paffrath <marcel.paffrath@ruhr-uni-bochum.de> <marcel.paffrath@rub.de>
|
||||
Marcel Paffrath <marcel.paffrath@ruhr-uni-bochum.de> <marcel@minos01.geophysik.ruhr-uni-bochum.de>
|
||||
Marcel Paffrath <marcel.paffrath@ruhr-uni-bochum.de> <marcel@minos02.geophysik.ruhr-uni-bochum.de>
|
||||
Marcel Paffrath <marcel.paffrath@ruhr-uni-bochum.de> <marcel@minos25.geophysik.ruhr-uni-bochum.de>
|
||||
Marcel Paffrath <marcel.paffrath@ruhr-uni-bochum.de> <marcel@email.com>
|
||||
|
||||
Sally Zimmermann <sally.zimmermann@ruhr-uni-bochum.de>
|
||||
|
||||
Sebastian Wehling-Benatelli <sebastian.wehling-benatelli@cgi.com> <sebastianw@minos01.geophysik.ruhr-uni-bochum.de>
|
||||
Sebastian Wehling-Benatelli <sebastian.wehling-benatelli@cgi.com> <sebastianw@minos02.geophysik.ruhr-uni-bochum.de>
|
||||
Sebastian Wehling-Benatelli <sebastian.wehling-benatelli@cgi.com> <sebastianw@minos22.geophysik.ruhr-uni-bochum.de>
|
||||
Sebastian Wehling-Benatelli <sebastian.wehling-benatelli@cgi.com> <sebastian.wehling-benatelli@scisys.de>
|
||||
Sebastian Wehling-Benatelli <sebastian.wehling-benatelli@cgi.com> <sebastian.wehling@rub.de>
|
||||
Sebastian Wehling-Benatelli <sebastian.wehling-benatelli@cgi.com> <sebastian.wehling@rub.de>
|
||||
Sebastian Wehling-Benatelli <sebastian.wehling-benatelli@cgi.com> <DarkBeQst@users.noreply.github.com>
|
||||
|
||||
Thomas Moeller <thomas.moeller@rub.de>
|
||||
|
||||
Ann-Christin Koch <ann-christin.koch@ruhr-uni-bochum.de> <Ann-Christin.Koch@ruhr-uni-bochum.de>
|
||||
|
||||
Sebastian Priebe <sebastian.priebe@rub.de>
|
||||
@@ -11,7 +11,7 @@ PILOT has originally been developed in Mathworks' MatLab. In order to distribute
|
||||
problems, it has been decided to redevelop the software package in Python. The great work of the ObsPy 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, AlpArray and AdriaArray.
|
||||
The development of PyLoT is part of the joint research project MAGS2 and AlpArray.
|
||||
|
||||
## Installation
|
||||
|
||||
@@ -27,44 +27,58 @@ Afterwards run (from the PyLoT main directory where the files *requirements.txt*
|
||||
conda env create -f pylot.yml
|
||||
or
|
||||
|
||||
conda create -c conda-forge --name pylot_311 python=3.11 --file requirements.txt
|
||||
conda create --name pylot_38 --file requirements.txt
|
||||
|
||||
to create a new Anaconda environment called *pylot_311*.
|
||||
to create a new Anaconda environment called "pylot_38".
|
||||
|
||||
Afterwards activate the environment by typing
|
||||
|
||||
conda activate pylot_311
|
||||
conda activate pylot_38
|
||||
|
||||
#### Prerequisites:
|
||||
|
||||
In order to run PyLoT you need to install:
|
||||
|
||||
- Python 3
|
||||
- cartopy
|
||||
- joblib
|
||||
- obspy
|
||||
- pyaml
|
||||
- pyqtgraph
|
||||
- pyside2
|
||||
- pyqtgraph
|
||||
- cartopy
|
||||
|
||||
(the following are already dependencies of the above packages):
|
||||
- scipy
|
||||
- numpy
|
||||
- matplotlib
|
||||
- matplotlib <= 3.3.x
|
||||
|
||||
#### Some handwork:
|
||||
|
||||
Some extra information on error estimates (just needed for reading old PILOT data) and the Richter magnitude scaling
|
||||
PyLoT needs a properties folder on your system to work. It should be situated in your home directory
|
||||
(on Windows usually C:/Users/*username*):
|
||||
|
||||
mkdir ~/.pylot
|
||||
|
||||
In the next step you have to copy some files to this directory:
|
||||
|
||||
*for local distance seismicity*
|
||||
|
||||
cp path-to-pylot/inputs/pylot_local.in ~/.pylot/pylot.in
|
||||
|
||||
*for regional distance seismicity*
|
||||
|
||||
cp path-to-pylot/inputs/pylot_regional.in ~/.pylot/pylot.in
|
||||
|
||||
*for global distance seismicity*
|
||||
|
||||
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
|
||||
|
||||
cp path-to-pylot/inputs/PILOT_TimeErrors.in path-to-pylot/inputs/richter_scaling.data ~/.pylot/
|
||||
|
||||
You may need to do some modifications to these files. Especially folder names should be reviewed.
|
||||
|
||||
PyLoT has been tested on Mac OSX (10.11), Debian Linux 8 and on Windows 10/11.
|
||||
|
||||
## Example Dataset
|
||||
An example dataset with waveform data, metadata and automatic picks in the obspy-dmt dataset format for testing the teleseismic picking can be found at https://zenodo.org/doi/10.5281/zenodo.13759803
|
||||
PyLoT has been tested on Mac OSX (10.11), Debian Linux 8 and on Windows 10.
|
||||
|
||||
## Release notes
|
||||
|
||||
@@ -73,7 +87,6 @@ An example dataset with waveform data, metadata and automatic picks in the obspy
|
||||
- event organisation in project files and waveform visualisation
|
||||
- consistent manual phase picking through predefined SNR dependant zoom level
|
||||
- consistent automatic phase picking routines using Higher Order Statistics, AIC and Autoregression
|
||||
- pick correlation correction for teleseismic waveforms
|
||||
- interactive tuning of auto-pick parameters
|
||||
- uniform uncertainty estimation from waveform's properties for automatic and manual picks
|
||||
- pdf representation and comparison of picks taking the uncertainty intrinsically into account
|
||||
@@ -82,17 +95,17 @@ An example dataset with waveform data, metadata and automatic picks in the obspy
|
||||
|
||||
#### Known issues:
|
||||
|
||||
Current release is still in development progress and has several issues. We are currently lacking manpower, but hope to assess many of the issues in the near future.
|
||||
We hope to solve these with the next release.
|
||||
|
||||
## Staff
|
||||
|
||||
Developer(s): M. Paffrath, S. Wehling-Benatelli, L. Kueperkoch, D. Arnold, K. Cökerim, K. Olbert, M. Bischoff, C. Wollin, M. Rische, S. Zimmermann
|
||||
|
||||
Original author(s): M. Rische, S. Wehling-Benatelli, L. Kueperkoch, M. Bischoff (PILOT)
|
||||
|
||||
Developer(s): S. Wehling-Benatelli, M. Paffrath, L. Kueperkoch, K. Olbert, M. Bischoff, C. Wollin, M. Rische, D. Arnold, K. Cökerim, S. Zimmermann
|
||||
|
||||
Others: A. Bruestle, T. Meier, W. Friederich
|
||||
|
||||
|
||||
[ObsPy]: http://github.com/obspy/obspy/wiki
|
||||
|
||||
March 2025
|
||||
April 2022
|
||||
|
||||
+15
-14
@@ -136,9 +136,11 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even
|
||||
if parameter.hasParam('datastructure'):
|
||||
# getting information on data structure
|
||||
datastructure = DATASTRUCTURE[parameter.get('datastructure')]()
|
||||
dsfields = {'dpath': parameter.get('datapath'),}
|
||||
dsfields = {'root': parameter.get('rootpath'),
|
||||
'dpath': parameter.get('datapath'),
|
||||
'dbase': parameter.get('database')}
|
||||
|
||||
exf = ['dpath']
|
||||
exf = ['root', 'dpath', 'dbase']
|
||||
|
||||
if parameter['eventID'] != '*' and fnames == 'None':
|
||||
dsfields['eventID'] = parameter['eventID']
|
||||
@@ -184,15 +186,15 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even
|
||||
if not input_dict:
|
||||
# started in production mode
|
||||
datapath = datastructure.expandDataPath()
|
||||
if fnames in [None, 'None'] and parameter['eventID'] == '*':
|
||||
if fnames == 'None' and parameter['eventID'] == '*':
|
||||
# multiple event processing
|
||||
# read each event in database
|
||||
events = [event for event in glob.glob(os.path.join(datapath, '*')) if
|
||||
(os.path.isdir(event) and not event.endswith('EVENTS-INFO'))]
|
||||
elif fnames in [None, 'None'] and parameter['eventID'] != '*' and not type(parameter['eventID']) == list:
|
||||
elif fnames == 'None' and parameter['eventID'] != '*' and not type(parameter['eventID']) == list:
|
||||
# single event processing
|
||||
events = glob.glob(os.path.join(datapath, parameter['eventID']))
|
||||
elif fnames in [None, 'None'] and type(parameter['eventID']) == list:
|
||||
elif fnames == 'None' and type(parameter['eventID']) == list:
|
||||
# multiple event processing
|
||||
events = []
|
||||
for eventID in parameter['eventID']:
|
||||
@@ -204,10 +206,12 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even
|
||||
locflag = 2
|
||||
else:
|
||||
# started in tune or interactive mode
|
||||
datapath = parameter['datapath']
|
||||
datapath = os.path.join(parameter['rootpath'],
|
||||
parameter['datapath'])
|
||||
events = []
|
||||
for eventID in eventid:
|
||||
events.append(os.path.join(datapath,
|
||||
parameter['database'],
|
||||
eventID))
|
||||
|
||||
if not events:
|
||||
@@ -234,15 +238,12 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even
|
||||
data.get_evt_data().path = eventpath
|
||||
print('Reading event data from filename {}...'.format(filename))
|
||||
except Exception as e:
|
||||
if type(e) == FileNotFoundError:
|
||||
print('Creating new event file.')
|
||||
else:
|
||||
print('Could not read event from file {}: {}'.format(filename, e))
|
||||
print('Could not read event from file {}: {}'.format(filename, e))
|
||||
data = Data()
|
||||
pylot_event = Event(eventpath) # event should be path to event directory
|
||||
data.setEvtData(pylot_event)
|
||||
if fnames in [None, 'None']:
|
||||
data.setWFData(glob.glob(os.path.join(event_datapath, '*')))
|
||||
if fnames == 'None':
|
||||
data.set_wf_data(glob.glob(os.path.join(datapath, event_datapath, '*')))
|
||||
# the following is necessary because within
|
||||
# multiple event processing no event ID is provided
|
||||
# in autopylot.in
|
||||
@@ -257,7 +258,7 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even
|
||||
now.minute)
|
||||
parameter.setParam(eventID=eventID)
|
||||
else:
|
||||
data.setWFData(fnames)
|
||||
data.set_wf_data(fnames)
|
||||
|
||||
eventpath = events[0]
|
||||
# now = datetime.datetime.now()
|
||||
@@ -267,7 +268,7 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even
|
||||
# now.hour,
|
||||
# now.minute)
|
||||
parameter.setParam(eventID=eventid)
|
||||
wfdat = data.getWFData() # all available streams
|
||||
wfdat = data.get_wf_data() # all available streams
|
||||
if not station == 'all':
|
||||
wfdat = wfdat.select(station=station)
|
||||
if not wfdat:
|
||||
|
||||
+2
-6
@@ -5,12 +5,8 @@
|
||||
#$ -pe smp 40
|
||||
##$ -l mem=3G
|
||||
#$ -l h_vmem=6G
|
||||
##$ -l os=*stretch
|
||||
#$ -q low.q@minos11,low.q@minos12,low.q@minos13,low.q@minos14,low.q@minos15
|
||||
#$ -l os=*stretch
|
||||
|
||||
conda activate pylot_311
|
||||
|
||||
#python ./autoPyLoT.py -i /home/marcel/.pylot/pylot_adriaarray_m5.0-5.4.in -c 20 -dmt processed
|
||||
#python ./autoPyLoT.py -i /home/marcel/.pylot/pylot_adriaarray_m5.4-5.7.in -c 20 -dmt processed
|
||||
#python ./autoPyLoT.py -i /home/marcel/.pylot/pylot_adriaarray_m5.7-6.0.in -c 20 -dmt processed
|
||||
python ./autoPyLoT.py -i /home/marcel/.pylot/pylot_adriaarray_m6.0-10.0.in -c 20 -dmt processed
|
||||
python ./autoPyLoT.py -i /home/marcel/.pylot/pylot_adriaarray.in -c 20 -dmt processed
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
# Pick-Correlation Correction
|
||||
|
||||
## Introduction
|
||||
Currently, the pick-correlation correction algorithm is not accessible from they PyLoT GUI. The main file *pick_correlation_correction.py* is located in the directory *pylot\correlation*.
|
||||
The program only works for an obspy dmt database structure.
|
||||
|
||||
The basic workflow of the algorithm is shown in the following diagram. The first step **(1)** is the normal (automatic) picking procedure in PyLoT. Everything from step **(2)** to **(5)** is part of the correlation correction algorithm.
|
||||
|
||||
*Note: The first step is not required in case theoretical onsets are used instead of external picks when the parameter use_taupy_onsets is set to True. However, an existing event quakeML (.xml) file generated by PyLoT might be required for each event in case not external picks are used.*
|
||||
|
||||

|
||||
|
||||
A detailed description of the algorithm can be found in the corresponding publication:
|
||||
|
||||
*Paffrath, M., Friederich, W., and the AlpArray and AlpArray-SWATH D Working Groups: Teleseismic P waves at the AlpArray seismic network: wave fronts, absolute travel times and travel-time residuals, Solid Earth, 12, 1635–1660, https://doi.org/10.5194/se-12-1635-2021, 2021.*
|
||||
|
||||
## How to use
|
||||
To use the program you have to call the main program providing two mandatory arguments: a path to the obspy dmt database folder *dmt_database_path* and the path to the PyLoT infile *pylot.in* for picking of the beam trace:
|
||||
|
||||
```python pick_correlation_correction.py dmt_database_path pylot.in```
|
||||
|
||||
By default, the parameter file *parameters.yaml* is used. You can use the command line option *--params* to specify a different parameter file and other optional arguments such as *-pd* for plotting detailed information or *-n 4* to use 4 cores for parallel processing:
|
||||
|
||||
```python pick_correlation_correction.py dmt_database_path pylot.in --params parameters_adriaarray.yaml -pd -n 4```
|
||||
|
||||
## Cross-Correlation Parameters
|
||||
|
||||
The program uses the parameters in the file *parameters.yaml* by default. You can use the command line option *--params* to specify a different parameter file. An example of the parameter file is provided in the *correlation\parameters.yaml* file.
|
||||
|
||||
In the top level of the parameter file the logging level *logging* can be set, as well as a list of pick phases *pick_phases* (e.g. ['P', 'S']).
|
||||
|
||||
For each pick phase the different parameters can be set in the first sub-level of the parameter file, e.g.:
|
||||
|
||||
```yaml
|
||||
logging: info
|
||||
pick_phases: ['P', 'S']
|
||||
|
||||
P:
|
||||
min_corr_stacking: 0.8
|
||||
min_corr_export: 0.6
|
||||
[...]
|
||||
|
||||
S:
|
||||
min_corr_stacking: 0.7
|
||||
[...]
|
||||
```
|
||||
|
||||
The following parameters are available:
|
||||
|
||||
|
||||
| Parameter Name | Description | Parameter Type |
|
||||
|--------------------------------|----------------------------------------------------------------------------------------------------|----------------|
|
||||
| min_corr_stacking | Minimum correlation coefficient for building beam trace | float |
|
||||
| min_corr_export | Minimum correlation coefficient for pick export | float |
|
||||
| min_stack | Minimum number of stations for building beam trace | int |
|
||||
| t_before | Correlation window before reference pick | float |
|
||||
| t_after | Correlation window after reference pick | float |
|
||||
| cc_maxlag | Maximum shift for initial correlation | float |
|
||||
| cc_maxlag2 | Maximum shift for second (final) correlation (also for calculating pick uncertainty) | float |
|
||||
| initial_pick_outlier_threshold | Threshold for excluding large outliers of initial (AIC) picks | float |
|
||||
| export_threshold | Automatically exclude all onsets which deviate more than this threshold from corrected taup onsets | float |
|
||||
| min_picks_export | Minimum number of correlated picks for export | int |
|
||||
| min_picks_autopylot | Minimum number of reference auto picks to continue with event | int |
|
||||
| check_RMS | Do RMS check to search for restitution errors (very experimental) | bool |
|
||||
| use_taupy_onsets | Use taupy onsets as reference picks instead of external picks | bool |
|
||||
| station_list | Use the following stations as reference for stacking | list[str] |
|
||||
| use_stacked_trace | Use existing stacked trace if found (spare re-computation) | bool |
|
||||
| data_dir | obspyDMT data subdirectory (e.g. 'raw', 'processed') | str |
|
||||
| pickfile_extension | Use quakeML files (PyLoT output) with the following extension | str |
|
||||
| dt_stacking | Time difference for stacking window (in seconds) | list[float] |
|
||||
| filter_options | Filter for first correlation (rough) | dict |
|
||||
| filter_options_final | Filter for second correlation (fine) | dict |
|
||||
| filter_type | Filter type (e.g. bandpass) | str |
|
||||
| sampfreq | Sampling frequency (in Hz) | float |
|
||||
|
||||
## Example Dataset
|
||||
An example dataset with waveform data, metadata and automatic picks in the obspy-dmt dataset format for testing can be found at https://zenodo.org/doi/10.5281/zenodo.13759803
|
||||
@@ -203,6 +203,8 @@ The meaning of the header entries is:
|
||||
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_)
|
||||
|
||||
### Saving projects
|
||||
|
||||
Save the current project from the menu with File->Save project or File->Save project as. PyLoT uses ``.plp`` files to
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 64 KiB |
+99
-100
@@ -6,123 +6,122 @@ A description of the parameters used for determining automatic picks.
|
||||
|
||||
Parameters applied to the traces before picking algorithm starts.
|
||||
|
||||
| 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. |
|
||||
| *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. |
|
||||
| *Bandpass | |
|
||||
| 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. |
|
||||
| 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. |
|
||||
| *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. |
|
||||
| *Bandpass
|
||||
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
|
||||
|
||||
Parameters used for determination of initial P pick.
|
||||
|
||||
| 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. |
|
||||
| * | |
|
||||
| AICtsmooth* | Average of samples in this time window will be used for smoothing of the AIC-CF. |
|
||||
| * | |
|
||||
| checkwinP* | Time in front of the global maximum of the HOS-CF in which to search for a second local extrema. |
|
||||
| *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. |
|
||||
| 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. |
|
||||
| *
|
||||
AICtsmooth* | Average of samples in this time window will be used for smoothing of the AIC-CF. |
|
||||
| *
|
||||
checkwinP* | Time in front of the global maximum of the HOS-CF in which to search for a second local extrema. |
|
||||
| *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
|
||||
|
||||
Parameters used for determination of initial S pick
|
||||
|
||||
| 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. |
|
||||
| * | |
|
||||
| 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. |
|
||||
| * | |
|
||||
| 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. |
|
||||
| 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. |
|
||||
| *
|
||||
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. |
|
||||
| *
|
||||
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
|
||||
|
||||
Parameters used for determination of precise P pick.
|
||||
|
||||
| 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*. | |
|
||||
| * | |
|
||||
| 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*). | |
|
||||
| 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*. |
|
||||
| *
|
||||
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
|
||||
|
||||
Parameters used for determination of precise S pick.
|
||||
|
||||
| Name | Description |
|
||||
|--------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| * | |
|
||||
| tdet2h* | Time window for determination of AR coefficients. |
|
||||
| * | |
|
||||
| tpred2h* | Time window in which the waveform is predicted using the determined AR parameters. |
|
||||
| *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. |
|
||||
| * | |
|
||||
| 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. |
|
||||
| Name | Description |
|
||||
|--------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| *
|
||||
tdet2h* | Time window for determination of AR coefficients. |
|
||||
| *
|
||||
tpred2h* | Time window in which the waveform is predicted using the determined AR parameters. |
|
||||
| *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. |
|
||||
| *
|
||||
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
|
||||
|
||||
Parameters used for checking quality and integrity of automatic picks.
|
||||
|
||||
| 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. |
|
||||
| * | |
|
||||
| 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. |
|
||||
| *minsiglength*, *noisefacor*. *minpercent* | Parameters for checking signal length. In the time window of size * |
|
||||
|
||||
| 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. |
|
||||
| *
|
||||
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. |
|
||||
| *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. |
|
||||
| *
|
||||
@@ -140,12 +139,12 @@ wdttolerance* | Maximum allowed deviation of S onset
|
||||
|
||||
Parameters for discrete quality classes.
|
||||
|
||||
| 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. |
|
||||
| *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. | |
|
||||
| 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. |
|
||||
| *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. |
|
||||
|
||||
|
||||
@@ -4,8 +4,10 @@
|
||||
%Parameters are optimized for %extent data sets!
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
#main settings#
|
||||
#datapath# %data path
|
||||
#eventID# %event ID for single event processing (* for all events found in datapath)
|
||||
#rootpath# %project path
|
||||
#datapath# %data path
|
||||
#database# %name of data base
|
||||
#eventID# %event ID for single event processing (* for all events found in database)
|
||||
#invdir# %full path to inventory or dataless-seed file
|
||||
PILOT #datastructure# %choose data structure
|
||||
True #apverbose# %choose 'True' or 'False' for terminal output
|
||||
@@ -41,7 +43,6 @@ global #extent# %extent of a
|
||||
1150.0 #sstop# %end time [s] after P-onset for calculating CF for S-picking
|
||||
True #use_taup# %use estimated traveltimes from TauPy for calculating windows for CF
|
||||
iasp91 #taup_model# %define TauPy model for traveltime estimation. Possible values: 1066a, 1066b, ak135, ak135f, herrin, iasp91, jb, prem, pwdk, sp6
|
||||
P,Pdiff #taup_phases# %Specify possible phases for TauPy (comma separated). See Obspy TauPy documentation for possible values.
|
||||
0.05 0.5 #bpz1# %lower/upper corner freq. of first band pass filter Z-comp. [Hz]
|
||||
0.001 0.5 #bpz2# %lower/upper corner freq. of second band pass filter Z-comp. [Hz]
|
||||
0.05 0.5 #bph1# %lower/upper corner freq. of first band pass filter H-comp. [Hz]
|
||||
|
||||
@@ -4,8 +4,10 @@
|
||||
%Parameters are optimized for %extent data sets!
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
#main settings#
|
||||
/DATA/Insheim/EVENT_DATA/LOCAL/2018.02_Insheim #datapath# %data path
|
||||
e0006.038.18 #eventID# %event ID for single event processing (* for all events found in datapath)
|
||||
/DATA/Insheim #rootpath# %project path
|
||||
EVENT_DATA/LOCAL #datapath# %data path
|
||||
2018.02_Insheim #database# %name of data base
|
||||
e0006.038.18 #eventID# %event ID for single event processing (* for all events found in database)
|
||||
/DATA/Insheim/STAT_INFO #invdir# %full path to inventory or dataless-seed file
|
||||
PILOT #datastructure# %choose data structure
|
||||
True #apverbose# %choose 'True' or 'False' for terminal output
|
||||
@@ -40,8 +42,7 @@ local #extent# %extent of a
|
||||
-0.5 #sstart# %start time [s] relative to P-onset for calculating CF for S-picking
|
||||
10.0 #sstop# %end time [s] after P-onset for calculating CF for S-picking
|
||||
False #use_taup# %use estimated traveltimes from TauPy for calculating windows for CF
|
||||
iasp91 #taup_model# %define TauPy model for traveltime estimation
|
||||
P #taup_phases# %Specify possible phases for TauPy (comma separated). See Obspy TauPy documentation for possible values.
|
||||
iasp91 #taup_model# %define TauPy model for traveltime estimation
|
||||
2.0 20.0 #bpz1# %lower/upper corner freq. of first band pass filter Z-comp. [Hz]
|
||||
2.0 30.0 #bpz2# %lower/upper corner freq. of second band pass filter Z-comp. [Hz]
|
||||
2.0 10.0 #bph1# %lower/upper corner freq. of first band pass filter H-comp. [Hz]
|
||||
|
||||
@@ -4,8 +4,10 @@
|
||||
%Parameters are optimized for %extent data sets!
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
#main settings#
|
||||
#datapath# %data path
|
||||
#eventID# %event ID for single event processing (* for all events found in datapath)
|
||||
#rootpath# %project path
|
||||
#datapath# %data path
|
||||
#database# %name of data base
|
||||
#eventID# %event ID for single event processing (* for all events found in database)
|
||||
#invdir# %full path to inventory or dataless-seed file
|
||||
PILOT #datastructure# %choose data structure
|
||||
True #apverbose# %choose 'True' or 'False' for terminal output
|
||||
@@ -40,8 +42,7 @@ local #extent# %extent of a
|
||||
-1.0 #sstart# %start time [s] relative to P-onset for calculating CF for S-picking
|
||||
10.0 #sstop# %end time [s] after P-onset for calculating CF for S-picking
|
||||
True #use_taup# %use estimated traveltimes from TauPy for calculating windows for CF
|
||||
iasp91 #taup_model# %define TauPy model for traveltime estimation
|
||||
P #taup_phases# %Specify possible phases for TauPy (comma separated). See Obspy TauPy documentation for possible values.
|
||||
iasp91 #taup_model# %define TauPy model for traveltime estimation
|
||||
2.0 10.0 #bpz1# %lower/upper corner freq. of first band pass filter Z-comp. [Hz]
|
||||
2.0 12.0 #bpz2# %lower/upper corner freq. of second band pass filter Z-comp. [Hz]
|
||||
2.0 8.0 #bph1# %lower/upper corner freq. of first band pass filter H-comp. [Hz]
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
name: pylot_311
|
||||
name: pylot_38
|
||||
channels:
|
||||
- conda-forge
|
||||
- defaults
|
||||
dependencies:
|
||||
- cartopy=0.23.0=py311hcf9f919_1
|
||||
- joblib=1.4.2=pyhd8ed1ab_0
|
||||
- obspy=1.4.1=py311he736701_3
|
||||
- pyaml=24.7.0=pyhd8ed1ab_0
|
||||
- pyqtgraph=0.13.7=pyhd8ed1ab_0
|
||||
- pyside2=5.15.8=py311h3d699ce_4
|
||||
- pytest=8.3.2=pyhd8ed1ab_0
|
||||
- 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
|
||||
+1
-1
@@ -9,7 +9,7 @@ PyLoT - the Python picking and Localization Tool
|
||||
|
||||
This python library contains a graphical user interfaces for picking
|
||||
seismic phases. This software needs ObsPy (http://github.com/obspy/obspy/wiki)
|
||||
and the Qt libraries to be installed first.
|
||||
and the Qt4 libraries to be installed first.
|
||||
|
||||
PILOT has been developed in Mathworks' MatLab. In order to distribute
|
||||
PILOT without facing portability problems, it has been decided to re-
|
||||
|
||||
+42
-654
@@ -1,665 +1,69 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import copy
|
||||
import logging
|
||||
import os
|
||||
from dataclasses import dataclass, field
|
||||
from typing import List, Union
|
||||
|
||||
from PySide2.QtWidgets import QMessageBox
|
||||
from obspy import read_events
|
||||
from obspy.core import read, Stream, UTCDateTime
|
||||
from obspy.core.event import Event as ObsPyEvent
|
||||
from obspy.io.sac import SacIOError
|
||||
from obspy import UTCDateTime
|
||||
|
||||
import pylot.core.loc.focmec as focmec
|
||||
import pylot.core.loc.hypodd as hypodd
|
||||
import pylot.core.loc.velest as velest
|
||||
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, \
|
||||
check_for_gaps_and_merge, trim_station_components, check_for_nan
|
||||
from pylot.core.io.event import EventData
|
||||
from pylot.core.io.waveformdata import WaveformData
|
||||
from pylot.core.util.dataprocessing import Metadata
|
||||
|
||||
@dataclass
|
||||
class Data:
|
||||
event_data: EventData = field(default_factory=EventData)
|
||||
waveform_data: WaveformData = field(default_factory=WaveformData)
|
||||
metadata: Metadata = field(default_factory=Metadata)
|
||||
_parent: Union[None, 'QtWidgets.QWidget'] = None
|
||||
|
||||
class Data(object):
|
||||
"""
|
||||
Data container with attributes wfdata holding ~obspy.core.stream.
|
||||
|
||||
:type parent: PySide2.QtWidgets.QWidget object, optional
|
||||
:param parent: A PySide2.QtWidgets.QWidget object utilized when
|
||||
called by a GUI to display a PySide2.QtWidgets.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, picking_parameter=None):
|
||||
def __init__(self, parent=None, evtdata=None):
|
||||
self._parent = parent
|
||||
|
||||
if not picking_parameter:
|
||||
if hasattr(parent, '_inputs'):
|
||||
picking_parameter = parent._inputs
|
||||
else:
|
||||
logging.warning('No picking parameters found! Using default input parameters!!!')
|
||||
picking_parameter = PylotParameter()
|
||||
self.picking_parameter = picking_parameter
|
||||
|
||||
if self.getParent():
|
||||
self.comp = parent.getComponent()
|
||||
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 isinstance(evtdata, str):
|
||||
try:
|
||||
cat = read_events(evtdata)
|
||||
if len(cat) != 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 '
|
||||
'implemented.')
|
||||
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
|
||||
self.event_data = EventData(evtdata)
|
||||
self.waveform_data = WaveformData()
|
||||
|
||||
def __str__(self):
|
||||
return str(self.wfdata)
|
||||
return str(self.waveform_data.wfdata)
|
||||
|
||||
def __add__(self, other):
|
||||
assert isinstance(other, Data), "operands must be of same type 'Data'"
|
||||
rs_id = self.get_evt_data().get('resource_id')
|
||||
rs_id_other = other.get_evt_data().get('resource_id')
|
||||
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():
|
||||
if not isinstance(other, Data):
|
||||
raise TypeError("Operands must be of type 'Data'")
|
||||
if self.event_data.is_new() and other.event_data.is_new():
|
||||
pass
|
||||
elif rs_id == rs_id_other:
|
||||
other.setNew()
|
||||
elif other.event_data.is_new():
|
||||
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
|
||||
else:
|
||||
raise ValueError("both Data objects have differing "
|
||||
"unique Event identifiers")
|
||||
raise ValueError("Both Data objects have differing unique Event identifiers")
|
||||
return self
|
||||
|
||||
def getPicksStr(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
|
||||
"""
|
||||
def get_parent(self):
|
||||
return self._parent
|
||||
|
||||
def isNew(self):
|
||||
return self._new
|
||||
def filter_wf_data(self, **kwargs):
|
||||
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):
|
||||
self._new = True
|
||||
def set_wf_data(self, fnames: List[str], fnames_alt: List[str] = None, check_rotated=False, metadata=None, tstart=0, tstop=0):
|
||||
return self.waveform_data.load_waveforms(fnames, fnames_alt, check_rotated, metadata, tstart, tstop)
|
||||
|
||||
def getCutTimes(self):
|
||||
"""
|
||||
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 reset_wf_data(self):
|
||||
self.waveform_data.reset()
|
||||
|
||||
def updateCutTimes(self):
|
||||
"""
|
||||
Update cuttimes to contain earliest start and latest end time
|
||||
of all waveform data
|
||||
:rtype: None
|
||||
"""
|
||||
self.cuttimes = full_range(self.getWFData())
|
||||
def get_wf_data(self):
|
||||
return self.waveform_data.wfdata
|
||||
|
||||
def getEventFileName(self):
|
||||
ID = self.getID()
|
||||
# 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 with own
|
||||
information if no other information are given 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 picks in event with own picks
|
||||
: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 = 1
|
||||
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 = 2
|
||||
except AttributeError as e:
|
||||
msg = '{}'.format(e)
|
||||
print(e)
|
||||
checkflag = 0
|
||||
if checkflag > 0:
|
||||
if checkflag == 1:
|
||||
print("Write new %s picks to catalog." % picktype)
|
||||
if checkflag == 2:
|
||||
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'):
|
||||
try:
|
||||
with open(os.path.join(os.path.dirname(fnout), 'notes.txt'), 'w') as notes_file:
|
||||
notes_file.write(self.get_evt_data().notes)
|
||||
except Exception as e:
|
||||
print('Warning: Could not save notes.txt: ', str(e))
|
||||
|
||||
# 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 != 0:
|
||||
print("Manual as well as automatic picks available. Prefered the {} manual ones!".format(lendiff))
|
||||
|
||||
|
||||
no_uncertainties_p = []
|
||||
no_uncertainties_s = []
|
||||
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':
|
||||
# Skipping pick if no upper_uncertainty is found and warning user
|
||||
if picks_copy[i].time_errors['upper_uncertainty'] is None:
|
||||
#print("{1} P-Pick of station {0} does not have upper_uncertainty and cant be checked".format(
|
||||
# picks_copy[i].waveform_id.station_code,
|
||||
# picks_copy[i].method_id))
|
||||
if not picks_copy[i].waveform_id.station_code in no_uncertainties_p:
|
||||
no_uncertainties_p.append(picks_copy[i].waveform_id.station_code)
|
||||
continue
|
||||
|
||||
#print ("checking for upper_uncertainty")
|
||||
if (picks_copy[i].time_errors['uncertainty'] is None) or \
|
||||
(picks_copy[i].time_errors['upper_uncertainty'] >= upperErrors[0]):
|
||||
print("Uncertainty exceeds or equal adjusted upper time error!")
|
||||
print("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))
|
||||
del picks_copy[i]
|
||||
break
|
||||
if picks_copy[i].phase_hint[0] == 'S':
|
||||
|
||||
# Skipping pick if no upper_uncertainty is found and warning user
|
||||
if picks_copy[i].time_errors['upper_uncertainty'] is None:
|
||||
#print("{1} S-Pick of station {0} does not have upper_uncertainty and cant be checked".format(
|
||||
#picks_copy[i].waveform_id.station_code,
|
||||
#picks_copy[i].method_id))
|
||||
if not picks_copy[i].waveform_id.station_code in no_uncertainties_s:
|
||||
no_uncertainties_s.append(picks_copy[i].waveform_id.station_code)
|
||||
continue
|
||||
|
||||
|
||||
if (picks_copy[i].time_errors['uncertainty'] is None) or \
|
||||
(picks_copy[i].time_errors['upper_uncertainty'] >= upperErrors[1]):
|
||||
print("Uncertainty exceeds or equal adjusted upper time error!")
|
||||
print("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))
|
||||
del picks_copy[i]
|
||||
break
|
||||
for s in no_uncertainties_p:
|
||||
print("P-Pick of station {0} does not have upper_uncertainty and cant be checked".format(s))
|
||||
for s in no_uncertainties_s:
|
||||
print("S-Pick of station {0} does not have upper_uncertainty and cant be checked".format(s))
|
||||
|
||||
if fnext == '.obs':
|
||||
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()
|
||||
# Adding A0/Generic Amplitude to .obs file
|
||||
# l2 = []
|
||||
# for li in l:
|
||||
# for amp in evtdata_org.amplitudes:
|
||||
# if amp.waveform_id.station_code == li[0:5].strip():
|
||||
# li = li[0:64] + '{:0.2e}'.format(amp.generic_amplitude) + li[73:-1] + '\n'
|
||||
# l2.append(li)
|
||||
# l = l2
|
||||
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, self.picking_parameter, 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:
|
||||
focmec.export(picks_copy, fnout + fnext, self.picking_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:
|
||||
hypodd.export(picks_copy, fnout + fnext, self.picking_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_alt=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
|
||||
:param fnames_alt: alternative data to show (e.g. synthetic/processed)
|
||||
:type fnames: list
|
||||
"""
|
||||
def check_fname_exists(filenames: list) -> list:
|
||||
if filenames:
|
||||
filenames = [fn for fn in filenames if os.path.isfile(fn)]
|
||||
return filenames
|
||||
|
||||
self.wfdata = Stream()
|
||||
self.wforiginal = None
|
||||
self.wf_alt = Stream()
|
||||
if tstart == tstop:
|
||||
tstart = tstop = None
|
||||
self.tstart = tstart
|
||||
self.tstop = tstop
|
||||
|
||||
# remove directories
|
||||
fnames = check_fname_exists(fnames)
|
||||
fnames_alt = check_fname_exists(fnames_alt)
|
||||
|
||||
# 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_alt is not None:
|
||||
self.appendWFData(fnames_alt, alternative=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, _ = check_for_gaps_and_merge(self.wfdata)
|
||||
# check for nans
|
||||
check_for_nan(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, alternative=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()
|
||||
|
||||
orig_or_alternative_data = {True: self.wf_alt,
|
||||
False: self.wfdata}
|
||||
|
||||
warnmsg = ''
|
||||
for fname in set(fnames):
|
||||
try:
|
||||
orig_or_alternative_data[alternative] += read(fname, starttime=self.tstart, endtime=self.tstop)
|
||||
except TypeError:
|
||||
try:
|
||||
orig_or_alternative_data[alternative] += read(fname, format='GSE2', starttime=self.tstart, endtime=self.tstop)
|
||||
except Exception as e:
|
||||
try:
|
||||
orig_or_alternative_data[alternative] += 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 getAltWFdata(self):
|
||||
return self.wf_alt
|
||||
|
||||
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'):
|
||||
"""
|
||||
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
|
||||
def rotate_wf_data(self):
|
||||
self.waveform_data.rotate_zne(self.metadata)
|
||||
|
||||
|
||||
class GenericDataStructure(object):
|
||||
@@ -844,22 +248,6 @@ class PilotDataStructure(GenericDataStructure):
|
||||
self.setExpandFields(['root', 'database'])
|
||||
|
||||
|
||||
class ObspyDMTdataStructure(GenericDataStructure):
|
||||
"""
|
||||
Object containing the data access information for the old PILOT data
|
||||
structure.
|
||||
"""
|
||||
|
||||
def __init__(self, **fields):
|
||||
if not fields:
|
||||
fields = {'database': '',
|
||||
'root': ''}
|
||||
|
||||
GenericDataStructure.__init__(self, **fields)
|
||||
|
||||
self.setExpandFields(['root', 'database'])
|
||||
|
||||
|
||||
class SeiscompDataStructure(GenericDataStructure):
|
||||
"""
|
||||
Dictionary containing the data access information for an SDS data archive:
|
||||
|
||||
@@ -6,14 +6,24 @@ import numpy as np
|
||||
Default parameters used for picking
|
||||
"""
|
||||
|
||||
defaults = {'datapath': {'type': str,
|
||||
'tooltip': 'path to eventfolders',
|
||||
defaults = {'rootpath': {'type': str,
|
||||
'tooltip': 'project path',
|
||||
'value': '',
|
||||
'namestring': 'Root path'},
|
||||
|
||||
'datapath': {'type': str,
|
||||
'tooltip': 'data path',
|
||||
'value': '',
|
||||
'namestring': 'Data path'},
|
||||
|
||||
'database': {'type': str,
|
||||
'tooltip': 'name of data base',
|
||||
'value': '',
|
||||
'namestring': 'Database path'},
|
||||
|
||||
'eventID': {'type': str,
|
||||
'tooltip': 'event ID for single event processing (* for all events found in datapath)',
|
||||
'value': '*',
|
||||
'tooltip': 'event ID for single event processing (* for all events found in database)',
|
||||
'value': '',
|
||||
'namestring': 'Event ID'},
|
||||
|
||||
'extent': {'type': str,
|
||||
@@ -501,7 +511,7 @@ defaults = {'datapath': {'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',
|
||||
'value': 'iasp91',
|
||||
'value': None,
|
||||
'namestring': 'TauPy model'},
|
||||
|
||||
'taup_phases': {'type': str,
|
||||
@@ -512,7 +522,9 @@ defaults = {'datapath': {'type': str,
|
||||
|
||||
settings_main = {
|
||||
'dirs': [
|
||||
'rootpath',
|
||||
'datapath',
|
||||
'database',
|
||||
'eventID',
|
||||
'invdir',
|
||||
'datastructure',
|
||||
|
||||
@@ -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}")
|
||||
+3
-33
@@ -1,7 +1,5 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
import logging
|
||||
import os
|
||||
|
||||
from pylot.core.io import default_parameters
|
||||
from pylot.core.util.errors import ParameterError
|
||||
@@ -53,16 +51,10 @@ class PylotParameter(object):
|
||||
self.__parameter = {}
|
||||
self._verbosity = verbosity
|
||||
self._parFileCont = {}
|
||||
|
||||
# io from parsed arguments alternatively
|
||||
for key, val in kwargs.items():
|
||||
self._parFileCont[key] = val
|
||||
self.from_file()
|
||||
|
||||
# if no filename or kwargs given, use default values
|
||||
if not fnin and not kwargs:
|
||||
self.reset_defaults()
|
||||
|
||||
if fnout:
|
||||
self.export2File(fnout)
|
||||
|
||||
@@ -96,10 +88,10 @@ class PylotParameter(object):
|
||||
return bool(self.__parameter)
|
||||
|
||||
def __getitem__(self, key):
|
||||
if key in self.__parameter:
|
||||
try:
|
||||
return self.__parameter[key]
|
||||
else:
|
||||
logging.warning(f'{key} not found in PylotParameter')
|
||||
except:
|
||||
return None
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
try:
|
||||
@@ -426,28 +418,6 @@ class PylotParameter(object):
|
||||
line = value + name + ttip
|
||||
fid.write(line)
|
||||
|
||||
@staticmethod
|
||||
def check_deprecated_parameters(parameters):
|
||||
if parameters.hasParam('database') and parameters.hasParam('rootpath'):
|
||||
parameters['datapath'] = os.path.join(parameters['rootpath'], parameters['datapath'],
|
||||
parameters['database'])
|
||||
logging.warning(
|
||||
f'Parameters database and rootpath are deprecated. '
|
||||
f'Tried to merge them to now path: {parameters["datapath"]}.'
|
||||
)
|
||||
|
||||
remove_keys = []
|
||||
for key in parameters:
|
||||
if not key in default_parameters.defaults.keys():
|
||||
remove_keys.append(key)
|
||||
logging.warning(f'Removing deprecated parameter: {key}')
|
||||
|
||||
for key in remove_keys:
|
||||
del parameters[key]
|
||||
|
||||
parameters._settings_main = default_parameters.settings_main
|
||||
parameters._settings_special_pick = default_parameters.settings_special_pick
|
||||
|
||||
|
||||
class FilterOptions(object):
|
||||
'''
|
||||
|
||||
+203
-654
@@ -21,25 +21,6 @@ from pylot.core.util.utils import get_owner, full_range, four_digits, transformF
|
||||
backtransformFilterString, loopIdentifyPhase, identifyPhase
|
||||
|
||||
|
||||
def add_amplitudes(event, amplitudes):
|
||||
amplitude_list = []
|
||||
for pick in event.picks:
|
||||
try:
|
||||
a0 = amplitudes[pick.waveform_id.station_code]
|
||||
amplitude = ope.Amplitude(generic_amplitude=a0 * 1e-3)
|
||||
amplitude.unit = 'm'
|
||||
amplitude.category = 'point'
|
||||
amplitude.waveform_id = pick.waveform_id
|
||||
amplitude.magnitude_hint = 'ML'
|
||||
amplitude.pick_id = pick.resource_id
|
||||
amplitude.type = 'AML'
|
||||
amplitude_list.append(amplitude)
|
||||
except KeyError:
|
||||
continue
|
||||
event.amplitudes = amplitude_list
|
||||
return event
|
||||
|
||||
|
||||
def readPILOTEvent(phasfn=None, locfn=None, authority_id='RUB', **kwargs):
|
||||
"""
|
||||
readPILOTEvent - function
|
||||
@@ -193,31 +174,6 @@ def convert_pilot_times(time_array):
|
||||
return UTCDateTime(*times)
|
||||
|
||||
|
||||
def picksdict_from_obs(fn):
|
||||
"""
|
||||
create pick dictionary from obs file
|
||||
:param fn: filename
|
||||
:type fn:
|
||||
:return:
|
||||
:rtype:
|
||||
"""
|
||||
picks = dict()
|
||||
station_name = str()
|
||||
for line in open(fn, 'r'):
|
||||
if line.startswith('#'):
|
||||
continue
|
||||
else:
|
||||
phase_line = line.split()
|
||||
if not station_name == phase_line[0]:
|
||||
phase = dict()
|
||||
station_name = phase_line[0]
|
||||
phase_name = phase_line[4].upper()
|
||||
pick = UTCDateTime(phase_line[6] + phase_line[7] + phase_line[8])
|
||||
phase[phase_name] = dict(mpp=pick, fm=phase_line[5])
|
||||
picks[station_name] = phase
|
||||
return picks
|
||||
|
||||
|
||||
def picksdict_from_picks(evt, parameter=None):
|
||||
"""
|
||||
Takes an Event object and return the pick dictionary commonly used within
|
||||
@@ -278,6 +234,7 @@ def picksdict_from_picks(evt, parameter=None):
|
||||
weight = phase.get('weight')
|
||||
if not weight:
|
||||
if not parameter:
|
||||
logging.warning('Using ')
|
||||
logging.warning('Using default input parameter')
|
||||
parameter = PylotParameter()
|
||||
pick.phase_hint = identifyPhase(pick.phase_hint)
|
||||
@@ -372,636 +329,228 @@ def picks_from_picksdict(picks, creation_info=None):
|
||||
return picks_list
|
||||
|
||||
|
||||
def reassess_pilot_db(root_dir, db_dir, out_dir=None, fn_param=None, verbosity=0):
|
||||
# TODO: change root to datapath
|
||||
db_root = os.path.join(root_dir, db_dir)
|
||||
evt_list = glob.glob1(db_root, 'e????.???.??')
|
||||
|
||||
for evt in evt_list:
|
||||
if verbosity > 0:
|
||||
print('Reassessing event {0}'.format(evt))
|
||||
reassess_pilot_event(root_dir, db_dir, evt, out_dir, fn_param, verbosity)
|
||||
|
||||
|
||||
def reassess_pilot_event(root_dir, db_dir, event_id, out_dir=None, fn_param=None, verbosity=0):
|
||||
from obspy import read
|
||||
|
||||
from pylot.core.io.inputs import PylotParameter
|
||||
from pylot.core.pick.utils import earllatepicker
|
||||
# TODO: change root to datapath
|
||||
|
||||
default = PylotParameter(fn_param, verbosity)
|
||||
|
||||
search_base = os.path.join(root_dir, db_dir, event_id)
|
||||
phases_file = glob.glob(os.path.join(search_base, 'PHASES.mat'))
|
||||
if not phases_file:
|
||||
return
|
||||
if verbosity > 1:
|
||||
print('Opening PILOT phases file: {fn}'.format(fn=phases_file[0]))
|
||||
picks_dict = picksdict_from_pilot(phases_file[0])
|
||||
if verbosity > 0:
|
||||
print('Dictionary read from PHASES.mat:\n{0}'.format(picks_dict))
|
||||
datacheck = list()
|
||||
info = None
|
||||
for station in picks_dict.keys():
|
||||
fn_pattern = os.path.join(search_base, '{0}*'.format(station))
|
||||
try:
|
||||
st = read(fn_pattern)
|
||||
except TypeError as e:
|
||||
if 'Unknown format for file' in e.message:
|
||||
try:
|
||||
st = read(fn_pattern, format='GSE2')
|
||||
except ValueError as e:
|
||||
if e.message == 'second must be in 0..59':
|
||||
info = 'A known Error was raised. Please find the list of corrupted files and double-check these files.'
|
||||
datacheck.append(fn_pattern + ' (time info)\n')
|
||||
continue
|
||||
else:
|
||||
raise ValueError(e.message)
|
||||
except Exception as e:
|
||||
if 'No file matching file pattern:' in e.message:
|
||||
if verbosity > 0:
|
||||
warnings.warn('no waveform data found for station {station}'.format(station=station),
|
||||
RuntimeWarning)
|
||||
datacheck.append(fn_pattern + ' (no data)\n')
|
||||
continue
|
||||
else:
|
||||
raise e
|
||||
else:
|
||||
raise e
|
||||
for phase in picks_dict[station].keys():
|
||||
try:
|
||||
mpp = picks_dict[station][phase]['mpp']
|
||||
except KeyError as e:
|
||||
print(e.message, station)
|
||||
continue
|
||||
sel_st = select_for_phase(st, phase)
|
||||
if not sel_st:
|
||||
msg = 'no waveform data found for station {station}'.format(station=station)
|
||||
warnings.warn(msg, RuntimeWarning)
|
||||
continue
|
||||
stime, etime = full_range(sel_st)
|
||||
rel_pick = mpp - stime
|
||||
epp, lpp, spe = earllatepicker(sel_st,
|
||||
default.get('nfac{0}'.format(phase)),
|
||||
default.get('tsnrz' if phase == 'P' else 'tsnrh'),
|
||||
Pick1=rel_pick,
|
||||
iplot=0,
|
||||
verbosity=0)
|
||||
if epp is None or lpp is None:
|
||||
continue
|
||||
epp = stime + epp
|
||||
lpp = stime + lpp
|
||||
min_diff = 3 * st[0].stats.delta
|
||||
if lpp - mpp < min_diff:
|
||||
lpp = mpp + min_diff
|
||||
if mpp - epp < min_diff:
|
||||
epp = mpp - min_diff
|
||||
picks_dict[station][phase] = dict(epp=epp, mpp=mpp, lpp=lpp, spe=spe)
|
||||
if datacheck:
|
||||
if info:
|
||||
if verbosity > 0:
|
||||
print(info + ': {0}'.format(search_base))
|
||||
fncheck = open(os.path.join(search_base, 'datacheck_list'), 'w')
|
||||
fncheck.writelines(datacheck)
|
||||
fncheck.close()
|
||||
del datacheck
|
||||
# create Event object for export
|
||||
evt = ope.Event(resource_id=event_id)
|
||||
evt.picks = picks_from_picksdict(picks_dict)
|
||||
# write phase information to file
|
||||
if not out_dir:
|
||||
fnout_prefix = os.path.join(root_dir, db_dir, event_id, 'PyLoT_{0}.'.format(event_id))
|
||||
else:
|
||||
out_dir = os.path.join(out_dir, db_dir)
|
||||
if not os.path.isdir(out_dir):
|
||||
os.makedirs(out_dir)
|
||||
fnout_prefix = os.path.join(out_dir, 'PyLoT_{0}.'.format(event_id))
|
||||
evt.write(fnout_prefix + 'xml', format='QUAKEML')
|
||||
|
||||
|
||||
def writephases(arrivals, fformat, filename, parameter=None, eventinfo=None):
|
||||
def write_phases(arrivals, fformat, filename, parameter=None, eventinfo=None):
|
||||
"""
|
||||
Function of methods to write phases to the following standard file
|
||||
formats used for locating earthquakes:
|
||||
Writes earthquake phase data to different file formats.
|
||||
|
||||
HYPO71, NLLoc, VELEST, HYPOSAT, FOCMEC, and hypoDD
|
||||
|
||||
:param arrivals:dictionary containing all phase information including
|
||||
station ID, phase, first motion, weight (uncertainty), ...
|
||||
:param arrivals: Dictionary containing phase information (station ID, phase, first motion, weight, etc.)
|
||||
:type arrivals: dict
|
||||
|
||||
:param fformat: chosen file format (location routine),
|
||||
choose between NLLoc, HYPO71, HYPOSAT, VELEST,
|
||||
HYPOINVERSE, FOCMEC, and hypoDD
|
||||
:param fformat: File format to write to (e.g., 'NLLoc', 'HYPO71', 'HYPOSAT', 'VELEST', 'HYPODD', 'FOCMEC')
|
||||
:type fformat: str
|
||||
|
||||
:param filename: full path and name of phase file
|
||||
:type filename: string
|
||||
|
||||
:param parameter: all input information
|
||||
:type parameter: object
|
||||
|
||||
:param eventinfo: optional, needed for VELEST-cnv file
|
||||
and FOCMEC- and HASH-input files
|
||||
:type eventinfo: `obspy.core.event.Event` object
|
||||
:param filename: Path and name of the output phase file
|
||||
:type filename: str
|
||||
:param parameter: Additional parameters for writing the phase data
|
||||
:type parameter: object
|
||||
:param eventinfo: Event information needed for specific formats like VELEST, FOCMEC, and HASH
|
||||
:type eventinfo: obspy.core.event.Event
|
||||
"""
|
||||
if fformat == 'NLLoc':
|
||||
print("Writing phases to %s for NLLoc" % filename)
|
||||
fid = open("%s" % filename, 'w')
|
||||
# write header
|
||||
fid.write('# EQEVENT: %s Label: EQ%s Loc: X 0.00 Y 0.00 Z 10.00 OT 0.00 \n' %
|
||||
(parameter.get('datapath'), parameter.get('eventID')))
|
||||
arrivals = chooseArrivals(arrivals)
|
||||
for key in arrivals:
|
||||
# P onsets
|
||||
if 'P' in arrivals[key]:
|
||||
try:
|
||||
fm = arrivals[key]['P']['fm']
|
||||
except KeyError as e:
|
||||
print(e)
|
||||
fm = None
|
||||
if fm is None:
|
||||
fm = '?'
|
||||
onset = arrivals[key]['P']['mpp']
|
||||
year = onset.year
|
||||
month = onset.month
|
||||
day = onset.day
|
||||
hh = onset.hour
|
||||
mm = onset.minute
|
||||
ss = onset.second
|
||||
ms = onset.microsecond
|
||||
ss_ms = ss + ms / 1000000.0
|
||||
pweight = 1 # use pick
|
||||
try:
|
||||
if arrivals[key]['P']['weight'] >= 4:
|
||||
pweight = 0 # do not use pick
|
||||
print("Station {}: Uncertain pick, do not use it!".format(key))
|
||||
except KeyError as e:
|
||||
print(e.message + '; no weight set during processing')
|
||||
fid.write('%s ? ? ? P %s %d%02d%02d %02d%02d %7.4f GAU 0 0 0 0 %d \n' % (key,
|
||||
fm,
|
||||
year,
|
||||
month,
|
||||
day,
|
||||
hh,
|
||||
mm,
|
||||
ss_ms,
|
||||
pweight))
|
||||
# S onsets
|
||||
if 'S' in arrivals[key] and arrivals[key]['S']['mpp'] is not None:
|
||||
fm = '?'
|
||||
onset = arrivals[key]['S']['mpp']
|
||||
year = onset.year
|
||||
month = onset.month
|
||||
day = onset.day
|
||||
hh = onset.hour
|
||||
mm = onset.minute
|
||||
ss = onset.second
|
||||
ms = onset.microsecond
|
||||
ss_ms = ss + ms / 1000000.0
|
||||
sweight = 1 # use pick
|
||||
try:
|
||||
if arrivals[key]['S']['weight'] >= 4:
|
||||
sweight = 0 # do not use pick
|
||||
except KeyError as e:
|
||||
print(str(e) + '; no weight set during processing')
|
||||
Ao = arrivals[key]['S']['Ao'] # peak-to-peak amplitude
|
||||
if Ao == None:
|
||||
Ao = 0.0
|
||||
# fid.write('%s ? ? ? S %s %d%02d%02d %02d%02d %7.4f GAU 0 0 0 0 %d \n' % (key,
|
||||
fid.write('%s ? ? ? S %s %d%02d%02d %02d%02d %7.4f GAU 0 %9.2f 0 0 %d \n' % (key,
|
||||
fm,
|
||||
year,
|
||||
month,
|
||||
day,
|
||||
hh,
|
||||
mm,
|
||||
ss_ms,
|
||||
Ao,
|
||||
sweight))
|
||||
|
||||
fid.close()
|
||||
elif fformat == 'HYPO71':
|
||||
print("Writing phases to %s for HYPO71" % filename)
|
||||
fid = open("%s" % filename, 'w')
|
||||
# write header
|
||||
fid.write(' %s\n' %
|
||||
parameter.get('eventID'))
|
||||
arrivals = chooseArrivals(arrivals) # MP MP what is chooseArrivals? It is not defined anywhere
|
||||
for key in arrivals:
|
||||
if arrivals[key]['P']['weight'] < 4:
|
||||
stat = key
|
||||
if len(stat) > 4: # HYPO71 handles only 4-string station IDs
|
||||
stat = stat[1:5]
|
||||
Ponset = arrivals[key]['P']['mpp']
|
||||
Sonset = arrivals[key]['S']['mpp']
|
||||
pweight = arrivals[key]['P']['weight']
|
||||
sweight = arrivals[key]['S']['weight']
|
||||
fm = arrivals[key]['P']['fm']
|
||||
if fm is None:
|
||||
fm = '-'
|
||||
Ao = arrivals[key]['S']['Ao']
|
||||
if Ao is None:
|
||||
Ao = ''
|
||||
else:
|
||||
Ao = str('%7.2f' % Ao)
|
||||
year = Ponset.year
|
||||
if year >= 2000:
|
||||
year = year - 2000
|
||||
else:
|
||||
year = year - 1900
|
||||
month = Ponset.month
|
||||
day = Ponset.day
|
||||
hh = Ponset.hour
|
||||
mm = Ponset.minute
|
||||
ss = Ponset.second
|
||||
ms = Ponset.microsecond
|
||||
ss_ms = ss + ms / 1000000.0
|
||||
if pweight < 2:
|
||||
pstr = 'I'
|
||||
elif pweight >= 2:
|
||||
pstr = 'E'
|
||||
if arrivals[key]['S']['weight'] < 4:
|
||||
Sss = Sonset.second
|
||||
Sms = Sonset.microsecond
|
||||
Sss_ms = Sss + Sms / 1000000.0
|
||||
Sss_ms = str('%5.02f' % Sss_ms)
|
||||
if sweight < 2:
|
||||
sstr = 'I'
|
||||
elif sweight >= 2:
|
||||
sstr = 'E'
|
||||
fid.write('%-4s%sP%s%d %02d%02d%02d%02d%02d%5.2f %s%sS %d %s\n' % (stat,
|
||||
pstr,
|
||||
fm,
|
||||
pweight,
|
||||
year,
|
||||
month,
|
||||
day,
|
||||
hh,
|
||||
mm,
|
||||
ss_ms,
|
||||
Sss_ms,
|
||||
sstr,
|
||||
sweight,
|
||||
Ao))
|
||||
else:
|
||||
fid.write('%-4s%sP%s%d %02d%02d%02d%02d%02d%5.2f %s\n' % (stat,
|
||||
pstr,
|
||||
fm,
|
||||
pweight,
|
||||
year,
|
||||
month,
|
||||
day,
|
||||
hh,
|
||||
mm,
|
||||
ss_ms,
|
||||
Ao))
|
||||
def write_nlloc():
|
||||
with open(filename, 'w') as fid:
|
||||
fid.write('# EQEVENT: {} Label: EQ{} Loc: X 0.00 Y 0.00 Z 10.00 OT 0.00 \n'.format(
|
||||
parameter.get('database'), parameter.get('eventID')))
|
||||
for key, value in arrivals.items():
|
||||
for phase in ['P', 'S']:
|
||||
if phase in value:
|
||||
fm = value[phase].get('fm', '?')
|
||||
onset = value[phase]['mpp']
|
||||
ss_ms = onset.second + onset.microsecond / 1000000.0
|
||||
weight = 1 if value[phase].get('weight', 0) < 4 else 0
|
||||
amp = value[phase].get('Ao', 0.0) if phase == 'S' else ''
|
||||
fid.write('{} ? ? ? {} {}{}{} {}{} {:7.4f} GAU 0 {} 0 0 {}\n'.format(
|
||||
key, phase, fm, onset.year, onset.month, onset.day, onset.hour, onset.minute, ss_ms, amp,
|
||||
weight))
|
||||
|
||||
fid.close()
|
||||
def write_hypo71():
|
||||
with open(filename, 'w') as fid:
|
||||
fid.write(
|
||||
' {}\n'.format(parameter.get('eventID')))
|
||||
for key, value in arrivals.items():
|
||||
if value['P'].get('weight', 0) < 4:
|
||||
stat = key[:4]
|
||||
Ponset = value['P']['mpp']
|
||||
Sonset = value.get('S', {}).get('mpp')
|
||||
pweight = value['P'].get('weight', 0)
|
||||
sweight = value.get('S', {}).get('weight', 0)
|
||||
fm = value['P'].get('fm', '-')
|
||||
Ao = value.get('S', {}).get('Ao', '')
|
||||
year = Ponset.year - 2000 if Ponset.year >= 2000 else Ponset.year - 1900
|
||||
ss_ms = Ponset.second + Ponset.microsecond / 1000000.0
|
||||
if Sonset:
|
||||
Sss_ms = Sonset.second + Sonset.microsecond / 1000000.0
|
||||
fid.write('{}P{}{}{} {}{}{}{}{} {:5.2f} {}{}S {} {}\n'.format(
|
||||
stat, 'I' if pweight < 2 else 'E', fm, pweight, year, Ponset.month, Ponset.day,
|
||||
Ponset.hour, Ponset.minute, ss_ms, Sss_ms, 'I' if sweight < 2 else 'E', sweight, Ao))
|
||||
else:
|
||||
fid.write('{}P{}{}{} {}{}{}{}{} {:5.2f} {}\n'.format(
|
||||
stat, 'I' if pweight < 2 else 'E', fm, pweight, year, Ponset.month, Ponset.day,
|
||||
Ponset.hour, Ponset.minute, ss_ms, Ao))
|
||||
|
||||
elif fformat == 'HYPOSAT':
|
||||
print("Writing phases to %s for HYPOSAT" % filename)
|
||||
fid = open("%s" % filename, 'w')
|
||||
# write header
|
||||
fid.write('%s, event %s \n' % (parameter.get('datapath'), parameter.get('eventID')))
|
||||
arrivals = chooseArrivals(arrivals)
|
||||
for key in arrivals:
|
||||
# P onsets
|
||||
if 'P' in arrivals[key] and arrivals[key]['P']['mpp'] is not None:
|
||||
if arrivals[key]['P']['weight'] < 4:
|
||||
Ponset = arrivals[key]['P']['mpp']
|
||||
pyear = Ponset.year
|
||||
pmonth = Ponset.month
|
||||
pday = Ponset.day
|
||||
phh = Ponset.hour
|
||||
pmm = Ponset.minute
|
||||
pss = Ponset.second
|
||||
pms = Ponset.microsecond
|
||||
Pss = pss + pms / 1000000.0
|
||||
# use symmetrized picking error as std
|
||||
# (read the HYPOSAT manual)
|
||||
pstd = arrivals[key]['P']['spe']
|
||||
if pstd is None:
|
||||
errorsP = parameter.get('timeerrorsP')
|
||||
if arrivals[key]['P']['weight'] == 0:
|
||||
pstd = errorsP[0]
|
||||
elif arrivals[key]['P']['weight'] == 1:
|
||||
pstd = errorsP[1]
|
||||
elif arrivals[key]['P']['weight'] == 2:
|
||||
pstd = errorsP[2]
|
||||
elif arrivals[key]['P']['weight'] == 3:
|
||||
psrd = errorsP[3]
|
||||
else:
|
||||
pstd = errorsP[4]
|
||||
fid.write('%-5s P1 %4.0f %02d %02d %02d %02d %05.02f %5.3f -999. 0.00 -999. 0.00\n'
|
||||
% (key, pyear, pmonth, pday, phh, pmm, Pss, pstd))
|
||||
# S onsets
|
||||
if 'S' in arrivals[key] and arrivals[key]['S']['mpp'] is not None:
|
||||
if arrivals[key]['S']['weight'] < 4:
|
||||
Sonset = arrivals[key]['S']['mpp']
|
||||
syear = Sonset.year
|
||||
smonth = Sonset.month
|
||||
sday = Sonset.day
|
||||
shh = Sonset.hour
|
||||
smm = Sonset.minute
|
||||
sss = Sonset.second
|
||||
sms = Sonset.microsecond
|
||||
Sss = sss + sms / 1000000.0
|
||||
sstd = arrivals[key]['S']['spe']
|
||||
if pstd is None:
|
||||
errorsS = parameter.get('timeerrorsS')
|
||||
if arrivals[key]['S']['weight'] == 0:
|
||||
pstd = errorsS[0]
|
||||
elif arrivals[key]['S']['weight'] == 1:
|
||||
pstd = errorsS[1]
|
||||
elif arrivals[key]['S']['weight'] == 2:
|
||||
pstd = errorsS[2]
|
||||
elif arrivals[key]['S']['weight'] == 3:
|
||||
psrd = errorsS[3]
|
||||
else:
|
||||
pstd = errorsP[4]
|
||||
fid.write('%-5s S1 %4.0f %02d %02d %02d %02d %05.02f %5.3f -999. 0.00 -999. 0.00\n'
|
||||
% (key, syear, smonth, sday, shh, smm, Sss, sstd))
|
||||
fid.close()
|
||||
def write_hyposat():
|
||||
with open(filename, 'w') as fid:
|
||||
fid.write('{}, event {} \n'.format(parameter.get('database'), parameter.get('eventID')))
|
||||
for key, value in arrivals.items():
|
||||
for phase in ['P', 'S']:
|
||||
if phase in value and value[phase].get('weight', 0) < 4:
|
||||
onset = value[phase]['mpp']
|
||||
ss_ms = onset.second + onset.microsecond / 1000000.0
|
||||
std = value[phase].get('spe', parameter.get('timeerrorsP')[value[phase].get('weight', 0)])
|
||||
fid.write(
|
||||
'{:<5} {}1 {:4} {:02} {:02} {:02} {:02} {:05.02f} {:5.3f} -999. 0.00 -999. 0.00\n'.format(
|
||||
key, phase, onset.year, onset.month, onset.day, onset.hour, onset.minute, ss_ms, std))
|
||||
|
||||
elif fformat == 'VELEST':
|
||||
print("Writing phases to %s for VELEST" % filename)
|
||||
fid = open("%s" % filename, 'w')
|
||||
# get informations needed in cnv-file
|
||||
# check, whether latitude is N or S and longitude is E or W
|
||||
try:
|
||||
eventsource = eventinfo.origins[0]
|
||||
except:
|
||||
def write_velest():
|
||||
if not eventinfo:
|
||||
print("No source origin calculated yet, thus no cnv-file creation possible!")
|
||||
return
|
||||
if eventsource['latitude'] < 0:
|
||||
cns = 'S'
|
||||
else:
|
||||
cns = 'N'
|
||||
if eventsource['longitude'] < 0:
|
||||
cew = 'W'
|
||||
else:
|
||||
cew = 'E'
|
||||
# get last two integers of origin year
|
||||
stime = eventsource['time']
|
||||
if stime.year - 2000 >= 0:
|
||||
syear = stime.year - 2000
|
||||
else:
|
||||
syear = stime.year - 1900
|
||||
ifx = 0 # default value, see VELEST manual, pp. 22-23
|
||||
# write header
|
||||
fid.write('%s%02d%02d %02d%02d %05.2f %7.4f%c %8.4f%c %7.2f %6.2f %02.0f 0.0 0.03 1.0 1.0\n' % (
|
||||
syear, stime.month, stime.day, stime.hour, stime.minute, stime.second, eventsource['latitude'],
|
||||
cns, eventsource['longitude'], cew, eventsource['depth'], eventinfo.magnitudes[0]['mag'], ifx))
|
||||
n = 0
|
||||
# check whether arrivals are dictionaries (autoPyLoT) or pick object (PyLoT)
|
||||
if isinstance(arrivals, dict) is False:
|
||||
# convert pick object (PyLoT) into dictionary
|
||||
evt = ope.Event(resource_id=eventinfo['resource_id'])
|
||||
evt.picks = arrivals
|
||||
arrivals = picksdict_from_picks(evt, parameter=parameter)
|
||||
# check for automatic and manual picks
|
||||
# prefer manual picks
|
||||
usedarrivals = chooseArrivals(arrivals)
|
||||
for key in usedarrivals:
|
||||
# P onsets
|
||||
if 'P' in usedarrivals[key]:
|
||||
if usedarrivals[key]['P']['weight'] < 4:
|
||||
n += 1
|
||||
stat = key
|
||||
if len(stat) > 4: # VELEST handles only 4-string station IDs
|
||||
stat = stat[1:5]
|
||||
Ponset = usedarrivals[key]['P']['mpp']
|
||||
Pweight = usedarrivals[key]['P']['weight']
|
||||
Prt = Ponset - stime # onset time relative to source time
|
||||
if n % 6 != 0:
|
||||
fid.write('%-4sP%d%6.2f' % (stat, Pweight, Prt))
|
||||
else:
|
||||
fid.write('%-4sP%d%6.2f\n' % (stat, Pweight, Prt))
|
||||
# S onsets
|
||||
if 'S' in usedarrivals[key]:
|
||||
if usedarrivals[key]['S']['weight'] < 4:
|
||||
n += 1
|
||||
stat = key
|
||||
if len(stat) > 4: # VELEST handles only 4-string station IDs
|
||||
stat = stat[1:5]
|
||||
Sonset = usedarrivals[key]['S']['mpp']
|
||||
Sweight = usedarrivals[key]['S']['weight']
|
||||
Srt = Ponset - stime # onset time relative to source time
|
||||
if n % 6 != 0:
|
||||
fid.write('%-4sS%d%6.2f' % (stat, Sweight, Srt))
|
||||
else:
|
||||
fid.write('%-4sS%d%6.2f\n' % (stat, Sweight, Srt))
|
||||
fid.close()
|
||||
with open(filename, 'w') as fid:
|
||||
origin = eventinfo.origins[0]
|
||||
lat_dir = 'S' if origin.latitude < 0 else 'N'
|
||||
lon_dir = 'W' if origin.longitude < 0 else 'E'
|
||||
year = origin.time.year - 2000 if origin.time.year >= 2000 else origin.time.year - 1900
|
||||
fid.write(
|
||||
'{}{}{} {}{} {} {:05.2f} {:7.4f}{} {:8.4f}{} {:7.2f} {:6.2f} {:02.0f} 0.0 0.03 1.0 1.0\n'.format(
|
||||
year, origin.time.month, origin.time.day, origin.time.hour, origin.time.minute, origin.time.second,
|
||||
origin.latitude, lat_dir, origin.longitude, lon_dir, origin.depth, eventinfo.magnitudes[0].mag, 0))
|
||||
for key, value in arrivals.items():
|
||||
for phase in ['P', 'S']:
|
||||
if phase in value and value[phase].get('weight', 0) < 4:
|
||||
onset = value[phase]['mpp']
|
||||
rt = (onset - origin.time).total_seconds()
|
||||
fid.write('{:<4}{}{}{:6.2f}\n'.format(key[:4], phase, value[phase].get('weight', 0), rt))
|
||||
|
||||
elif fformat == 'HYPODD':
|
||||
print("Writing phases to %s for hypoDD" % filename)
|
||||
fid = open("%s" % filename, 'w')
|
||||
# get event information needed for hypoDD-phase file
|
||||
try:
|
||||
eventsource = eventinfo.origins[0]
|
||||
except:
|
||||
def write_hypodd():
|
||||
if not eventinfo:
|
||||
print("No source origin calculated yet, thus no hypoDD-infile creation possible!")
|
||||
return
|
||||
stime = eventsource['time']
|
||||
try:
|
||||
event = eventinfo['pylot_id']
|
||||
hddID = event.split('.')[0][1:5]
|
||||
except:
|
||||
print("Error 1111111!")
|
||||
hddID = "00000"
|
||||
# write header
|
||||
fid.write('# %d %d %d %d %d %5.2f %7.4f +%6.4f %7.4f %4.2f 0.1 0.5 %4.2f %s\n' % (
|
||||
stime.year, stime.month, stime.day, stime.hour, stime.minute, stime.second,
|
||||
eventsource['latitude'], eventsource['longitude'], eventsource['depth'] / 1000,
|
||||
eventinfo.magnitudes[0]['mag'], eventsource['quality']['standard_error'], hddID))
|
||||
# check whether arrivals are dictionaries (autoPyLoT) or pick object (PyLoT)
|
||||
if isinstance(arrivals, dict) == False:
|
||||
# convert pick object (PyLoT) into dictionary
|
||||
evt = ope.Event(resource_id=eventinfo['resource_id'])
|
||||
evt.picks = arrivals
|
||||
arrivals = picksdict_from_picks(evt, parameter=parameter)
|
||||
# check for automatic and manual picks
|
||||
# prefer manual picks
|
||||
usedarrivals = chooseArrivals(arrivals)
|
||||
for key in usedarrivals:
|
||||
if 'P' in usedarrivals[key]:
|
||||
# P onsets
|
||||
if usedarrivals[key]['P']['weight'] < 4:
|
||||
Ponset = usedarrivals[key]['P']['mpp']
|
||||
Prt = Ponset - stime # onset time relative to source time
|
||||
fid.write('%s %6.3f 1 P\n' % (key, Prt))
|
||||
if 'S' in usedarrivals[key]:
|
||||
# S onsets
|
||||
if usedarrivals[key]['S']['weight'] < 4:
|
||||
Sonset = usedarrivals[key]['S']['mpp']
|
||||
Srt = Sonset - stime # onset time relative to source time
|
||||
fid.write('%-5s %6.3f 1 S\n' % (key, Srt))
|
||||
with open(filename, 'w') as fid:
|
||||
origin = eventinfo.origins[0]
|
||||
stime = origin.time
|
||||
fid.write('# {} {} {} {} {} {} {:7.4f} +{:6.4f} {:7.4f} {:4.2f} 0.1 0.5 {:4.2f} {}\n'.format(
|
||||
stime.year, stime.month, stime.day, stime.hour, stime.minute, stime.second,
|
||||
origin.latitude, origin.longitude, origin.depth / 1000, eventinfo.magnitudes[0].mag,
|
||||
origin.quality.standard_error, "00000"))
|
||||
for key, value in arrivals.items():
|
||||
for phase in ['P', 'S']:
|
||||
if phase in value and value[phase].get('weight', 0) < 4:
|
||||
onset = value[phase]['mpp']
|
||||
rt = (onset - stime).total_seconds()
|
||||
fid.write('{} {:6.3f} 1 {}\n'.format(key, rt, phase))
|
||||
|
||||
fid.close()
|
||||
|
||||
elif fformat == 'FOCMEC':
|
||||
print("Writing phases to %s for FOCMEC" % filename)
|
||||
fid = open("%s" % filename, 'w')
|
||||
# get event information needed for FOCMEC-input file
|
||||
try:
|
||||
eventsource = eventinfo.origins[0]
|
||||
except:
|
||||
def write_focmec():
|
||||
if not eventinfo:
|
||||
print("No source origin calculated yet, thus no FOCMEC-infile creation possible!")
|
||||
return
|
||||
stime = eventsource['time']
|
||||
|
||||
# avoid printing '*' in focmec-input file
|
||||
if parameter.get('eventid') == '*' or parameter.get('eventid') is None:
|
||||
evID = 'e0000'
|
||||
else:
|
||||
evID = parameter.get('eventid')
|
||||
|
||||
# write header line including event information
|
||||
fid.write('%s %d%02d%02d%02d%02d%02.0f %7.4f %6.4f %3.1f %3.1f\n' % (evID,
|
||||
stime.year, stime.month, stime.day,
|
||||
stime.hour, stime.minute, stime.second,
|
||||
eventsource['latitude'],
|
||||
eventsource['longitude'],
|
||||
eventsource['depth'] / 1000,
|
||||
eventinfo.magnitudes[0]['mag']))
|
||||
picks = eventinfo.picks
|
||||
# check whether arrivals are dictionaries (autoPyLoT) or pick object (PyLoT)
|
||||
if isinstance(arrivals, dict) == False:
|
||||
# convert pick object (PyLoT) into dictionary
|
||||
evt = ope.Event(resource_id=eventinfo['resource_id'])
|
||||
evt.picks = arrivals
|
||||
arrivals = picksdict_from_picks(evt, parameter=parameter)
|
||||
# check for automatic and manual picks
|
||||
# prefer manual picks
|
||||
usedarrivals = chooseArrivals(arrivals)
|
||||
for key in usedarrivals:
|
||||
if 'P' in usedarrivals[key]:
|
||||
if usedarrivals[key]['P']['weight'] < 4 and usedarrivals[key]['P']['fm'] is not None:
|
||||
stat = key
|
||||
for i in range(len(picks)):
|
||||
station = picks[i].waveform_id.station_code
|
||||
if station == stat:
|
||||
# get resource ID
|
||||
resid_picks = picks[i].get('resource_id')
|
||||
# find same ID in eventinfo
|
||||
# there it is the pick_id!!
|
||||
for j in range(len(eventinfo.origins[0].arrivals)):
|
||||
resid_eventinfo = eventinfo.origins[0].arrivals[j].get('pick_id')
|
||||
if resid_eventinfo == resid_picks and eventinfo.origins[0].arrivals[j].phase == 'P':
|
||||
if len(stat) > 4: # FOCMEC handles only 4-string station IDs
|
||||
stat = stat[1:5]
|
||||
az = eventinfo.origins[0].arrivals[j].get('azimuth')
|
||||
inz = eventinfo.origins[0].arrivals[j].get('takeoff_angle')
|
||||
fid.write('%-4s %6.2f %6.2f%s \n' % (stat,
|
||||
az,
|
||||
inz,
|
||||
usedarrivals[key]['P']['fm']))
|
||||
with open(filename, 'w') as fid:
|
||||
origin = eventinfo.origins[0]
|
||||
stime = origin.time
|
||||
fid.write('{} {}{:02d}{:02d}{:02d}{:02d}{:02.0f} {:7.4f} {:6.4f} {:3.1f} {:3.1f}\n'.format(
|
||||
parameter.get('eventid', 'e0000'), stime.year, stime.month, stime.day, stime.hour, stime.minute,
|
||||
stime.second, origin.latitude, origin.longitude, origin.depth / 1000, eventinfo.magnitudes[0].mag))
|
||||
for key, value in arrivals.items():
|
||||
if 'P' in value and value['P'].get('weight', 0) < 4 and value['P'].get('fm'):
|
||||
for pick in eventinfo.picks:
|
||||
if pick.waveform_id.station_code == key:
|
||||
for arrival in origin.arrivals:
|
||||
if arrival.pick_id == pick.resource_id and arrival.phase == 'P':
|
||||
stat = key[:4]
|
||||
az = arrival.azimuth
|
||||
inz = arrival.takeoff_angle
|
||||
fid.write('{:<4} {:6.2f} {:6.2f}{}\n'.format(stat, az, inz, value['P']['fm']))
|
||||
break
|
||||
|
||||
fid.close()
|
||||
def write_hash():
|
||||
# Define filenames for HASH driver 1 and 2
|
||||
filename1 = f"{filename}drv1.phase"
|
||||
filename2 = f"{filename}drv2.phase"
|
||||
|
||||
print(f"Writing phases to {filename1} for HASH-driver 1")
|
||||
print(f"Writing phases to {filename2} for HASH-driver 2")
|
||||
|
||||
# Open files for writing
|
||||
with open(filename1, 'w') as fid1, open(filename2, 'w') as fid2:
|
||||
# Get event information needed for HASH-input file
|
||||
try:
|
||||
eventsource = eventinfo.origins[0]
|
||||
except IndexError:
|
||||
print("No source origin calculated yet, thus no cnv-file creation possible!")
|
||||
return
|
||||
|
||||
event = parameter.get('eventID')
|
||||
hashID = event.split('.')[0][1:5]
|
||||
latdeg = eventsource['latitude']
|
||||
latmin = (eventsource['latitude'] * 60) / 10000
|
||||
londeg = eventsource['longitude']
|
||||
lonmin = (eventsource['longitude'] * 60) / 10000
|
||||
|
||||
erh = (eventsource.origin_uncertainty['min_horizontal_uncertainty'] +
|
||||
eventsource.origin_uncertainty['max_horizontal_uncertainty']) / 2000
|
||||
erz = eventsource.depth_errors['uncertainty']
|
||||
|
||||
stime = eventsource['time']
|
||||
syear = stime.year % 100 # Calculate two-digit year
|
||||
|
||||
picks = eventinfo.picks
|
||||
|
||||
# Write header line including event information for HASH-driver 1
|
||||
fid1.write(f"{syear:02d}{stime.month:02d}{stime.day:02d}{stime.hour:02d}{stime.minute:02d}"
|
||||
f"{stime.second:05.2f}{latdeg:2d}N{latmin:05.2f}{londeg:3d}E{lonmin:05.2f}"
|
||||
f"{eventsource['depth']:6.2f}{eventinfo.magnitudes[0]['mag']:4.2f}{erh:5.2f}{erz:5.2f}{hashID}\n")
|
||||
|
||||
# Write header line including event information for HASH-driver 2
|
||||
fid2.write(f"{syear:02d}{stime.month:02d}{stime.day:02d}{stime.hour:02d}{stime.minute:02d}"
|
||||
f"{stime.second:05.2f}{latdeg}N{latmin:05.2f}{londeg}E{lonmin:6.2f}{eventsource['depth']:5.2f}"
|
||||
f"{eventsource['quality']['used_phase_count']:3d}{erh:5.2f}{erz:5.2f}"
|
||||
f"{eventinfo.magnitudes[0]['mag']:4.2f}{hashID}\n")
|
||||
|
||||
# Write phase lines
|
||||
for key, arrival in arrivals.items():
|
||||
if 'P' in arrival and arrival['P']['weight'] < 4 and arrival['P']['fm'] is not None:
|
||||
stat = key
|
||||
ccode = arrival['P']['channel']
|
||||
ncode = arrival['P']['network']
|
||||
Pqual = 'I' if arrival['P']['weight'] < 2 else 'E'
|
||||
|
||||
for pick in picks:
|
||||
if pick.waveform_id.station_code == stat:
|
||||
resid_picks = pick.get('resource_id')
|
||||
for origin_arrival in eventinfo.origins[0].arrivals:
|
||||
if (origin_arrival.get('pick_id') == resid_picks and
|
||||
origin_arrival.phase == 'P'):
|
||||
if len(stat) > 4: # HASH handles only 4-character station IDs
|
||||
stat = stat[1:5]
|
||||
|
||||
az = origin_arrival.get('azimuth')
|
||||
inz = origin_arrival.get('takeoff_angle')
|
||||
dist = origin_arrival.get('distance')
|
||||
|
||||
# Write phase line for HASH-driver 1
|
||||
fid1.write(f"{stat:<4}{Pqual}P{arrival['P']['fm']}{arrival['P']['weight']:d}"
|
||||
f"{dist:3.1f}{inz:03d}{az:03d}{ccode}\n")
|
||||
|
||||
# Write phase line for HASH-driver 2
|
||||
fid2.write(f"{stat:<4} {ncode} {ccode} {Pqual} {arrival['P']['fm']}\n")
|
||||
break
|
||||
|
||||
fid1.write(f"{'':<36}{hashID}")
|
||||
|
||||
# Prefer Manual Picks over automatic ones if possible
|
||||
arrivals = chooseArrivals(arrivals) # Function not defined, assumed to exist
|
||||
|
||||
if fformat == 'NLLoc':
|
||||
write_nlloc()
|
||||
elif fformat == 'HYPO71':
|
||||
write_hypo71()
|
||||
elif fformat == 'HYPOSAT':
|
||||
write_hyposat()
|
||||
elif fformat == 'VELEST':
|
||||
write_velest()
|
||||
elif fformat == 'HYPODD':
|
||||
write_hypodd()
|
||||
elif fformat == 'FOCMEC':
|
||||
write_focmec()
|
||||
elif fformat == 'HASH':
|
||||
# two different input files for
|
||||
# HASH-driver 1 and 2 (see HASH manual!)
|
||||
filename1 = filename + 'drv1' + '.phase'
|
||||
filename2 = filename + 'drv2' + '.phase'
|
||||
print("Writing phases to %s for HASH for HASH-driver 1" % filename1)
|
||||
fid1 = open("%s" % filename1, 'w')
|
||||
print("Writing phases to %s for HASH for HASH-driver 2" % filename2)
|
||||
fid2 = open("%s" % filename2, 'w')
|
||||
# get event information needed for HASH-input file
|
||||
try:
|
||||
eventsource = eventinfo.origins[0]
|
||||
except:
|
||||
print("No source origin calculated yet, thus no cnv-file creation possible!")
|
||||
return
|
||||
eventsource = eventinfo.origins[0]
|
||||
event = parameter.get('eventID')
|
||||
hashID = event.split('.')[0][1:5]
|
||||
latdeg = eventsource['latitude']
|
||||
latmin = eventsource['latitude'] * 60 / 10000
|
||||
londeg = eventsource['longitude']
|
||||
lonmin = eventsource['longitude'] * 60 / 10000
|
||||
erh = 1 / 2 * (eventsource.origin_uncertainty['min_horizontal_uncertainty'] +
|
||||
eventsource.origin_uncertainty['max_horizontal_uncertainty']) / 1000
|
||||
erz = eventsource.depth_errors['uncertainty']
|
||||
stime = eventsource['time']
|
||||
if stime.year - 2000 >= 0:
|
||||
syear = stime.year - 2000
|
||||
else:
|
||||
syear = stime.year - 1900
|
||||
picks = eventinfo.picks
|
||||
# write header line including event information
|
||||
# for HASH-driver 1
|
||||
fid1.write('%s%02d%02d%02d%02d%5.2f%2dN%5.2f%3dE%5.2f%6.3f%4.2f%5.2f%5.2f%s\n' % (syear,
|
||||
stime.month, stime.day,
|
||||
stime.hour, stime.minute,
|
||||
stime.second,
|
||||
latdeg, latmin, londeg,
|
||||
lonmin, eventsource['depth'],
|
||||
eventinfo.magnitudes[0][
|
||||
'mag'], erh, erz,
|
||||
hashID))
|
||||
# write header line including event information
|
||||
# for HASH-driver 2
|
||||
fid2.write(
|
||||
'%d%02d%02d%02d%02d%5.2f%dN%5.2f%3dE%6.2f%5.2f %d %5.2f %5.2f %4.2f %s \n' % (
|
||||
syear, stime.month, stime.day,
|
||||
stime.hour, stime.minute, stime.second,
|
||||
latdeg, latmin, londeg, lonmin,
|
||||
eventsource['depth'],
|
||||
eventsource['quality']['used_phase_count'],
|
||||
erh, erz, eventinfo.magnitudes[0]['mag'],
|
||||
hashID))
|
||||
# Prefer Manual Picks over automatic ones if possible
|
||||
arrivals = chooseArrivals(arrivals) # MP MP what is chooseArrivals? It is not defined anywhere
|
||||
# write phase lines
|
||||
for key in arrivals:
|
||||
if 'P' in arrivals[key]:
|
||||
if arrivals[key]['P']['weight'] < 4 and arrivals[key]['P']['fm'] is not None:
|
||||
stat = key
|
||||
ccode = arrivals[key]['P']['channel']
|
||||
ncode = arrivals[key]['P']['network']
|
||||
|
||||
if arrivals[key]['P']['weight'] < 2:
|
||||
Pqual = 'I'
|
||||
else:
|
||||
Pqual = 'E'
|
||||
|
||||
for i in range(len(picks)):
|
||||
station = picks[i].waveform_id.station_code
|
||||
if station == stat:
|
||||
# get resource ID
|
||||
resid_picks = picks[i].get('resource_id')
|
||||
# find same ID in eventinfo
|
||||
# there it is the pick_id!!
|
||||
for j in range(len(eventinfo.origins[0].arrivals)):
|
||||
resid_eventinfo = eventinfo.origins[0].arrivals[j].get('pick_id')
|
||||
if resid_eventinfo == resid_picks and eventinfo.origins[0].arrivals[j].phase == 'P':
|
||||
if len(stat) > 4: # HASH handles only 4-string station IDs
|
||||
stat = stat[1:5]
|
||||
az = eventinfo.origins[0].arrivals[j].get('azimuth')
|
||||
inz = eventinfo.origins[0].arrivals[j].get('takeoff_angle')
|
||||
dist = eventinfo.origins[0].arrivals[j].get('distance')
|
||||
# write phase line for HASH-driver 1
|
||||
fid1.write(
|
||||
'%-4s%sP%s%d 0 %3.1f %03d %03d 2 1 %s\n' % (
|
||||
stat, Pqual, arrivals[key]['P']['fm'], arrivals[key]['P']['weight'],
|
||||
dist, inz, az, ccode))
|
||||
# write phase line for HASH-driver 2
|
||||
fid2.write('%-4s %s %s %s %s \n' % (
|
||||
stat,
|
||||
ncode,
|
||||
ccode,
|
||||
Pqual,
|
||||
arrivals[key]['P']['fm']))
|
||||
break
|
||||
|
||||
fid1.write(' %s' % hashID)
|
||||
fid1.close()
|
||||
fid2.close()
|
||||
write_hash()
|
||||
|
||||
|
||||
def chooseArrivals(arrivals):
|
||||
@@ -1121,7 +670,7 @@ def getQualitiesfromxml(path, errorsP, errorsS, plotflag=1, figure=None, verbosi
|
||||
mstation = pick.waveform_id.station_code
|
||||
mstation_ext = mstation + '_'
|
||||
for mpick in arrivals_copy:
|
||||
phase = identifyPhase(loopIdentifyPhase(pick.phase_hint)) # MP MP catch if this fails?
|
||||
phase = identifyPhase(loopIdentifyPhase(pick.phase_hint)) # MP MP catch if this fails?
|
||||
if ((mpick.waveform_id.station_code == mstation) or
|
||||
(mpick.waveform_id.station_code == mstation_ext)) and \
|
||||
(mpick.method_id.id.split('/')[1] == 'auto') and \
|
||||
|
||||
@@ -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
|
||||
# -*- 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
|
||||
|
||||
__version__ = _getVersionString()
|
||||
@@ -25,4 +25,4 @@ def export(picks, fnout, parameter, eventinfo):
|
||||
:type eventinfo: list object
|
||||
'''
|
||||
# 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
|
||||
# -*- 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
|
||||
|
||||
__version__ = _getVersionString()
|
||||
@@ -25,4 +25,4 @@ def export(picks, fnout, parameter, eventinfo):
|
||||
:type eventinfo: list object
|
||||
'''
|
||||
# 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
|
||||
# -*- 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
|
||||
|
||||
__version__ = _getVersionString()
|
||||
@@ -22,4 +22,4 @@ def export(picks, fnout, parameter):
|
||||
:type parameter: object
|
||||
'''
|
||||
# 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
|
||||
# -*- 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
|
||||
|
||||
__version__ = _getVersionString()
|
||||
@@ -25,4 +25,4 @@ def export(picks, fnout, parameter, eventinfo):
|
||||
:type eventinfo: list object
|
||||
'''
|
||||
# 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
|
||||
# -*- 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
|
||||
|
||||
__version__ = _getVersionString()
|
||||
@@ -22,4 +22,4 @@ def export(picks, fnout, parameter):
|
||||
:type parameter: object
|
||||
'''
|
||||
# write phases to HYPOSAT-phase file
|
||||
writephases(picks, 'HYPOSAT', fnout, parameter)
|
||||
write_phases(picks, 'HYPOSAT', fnout, parameter)
|
||||
|
||||
@@ -7,7 +7,7 @@ import subprocess
|
||||
|
||||
from obspy import read_events
|
||||
|
||||
from pylot.core.io.phases import writephases
|
||||
from pylot.core.io.phases import write_phases
|
||||
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
|
||||
@@ -34,7 +34,7 @@ def export(picks, fnout, parameter):
|
||||
:type parameter: object
|
||||
'''
|
||||
# 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):
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- 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
|
||||
|
||||
__version__ = _getVersionString()
|
||||
@@ -25,4 +25,4 @@ def export(picks, fnout, eventinfo, parameter=None):
|
||||
:type parameter: object
|
||||
'''
|
||||
# write phases to VELEST-phase file
|
||||
writephases(picks, 'VELEST', fnout, parameter, eventinfo)
|
||||
write_phases(picks, 'VELEST', fnout, parameter, eventinfo)
|
||||
|
||||
+17
-50
@@ -262,10 +262,6 @@ class AutopickStation(object):
|
||||
self.metadata = metadata
|
||||
self.origin = origin
|
||||
|
||||
# initialize TauPy pick estimates
|
||||
self.estFirstP = None
|
||||
self.estFirstS = None
|
||||
|
||||
# initialize picking results
|
||||
self.p_results = PickingResults()
|
||||
self.s_results = PickingResults()
|
||||
@@ -447,15 +443,15 @@ class AutopickStation(object):
|
||||
for arr in arrivals:
|
||||
phases[identifyPhaseID(arr.phase.name)].append(arr)
|
||||
# get first P and S onsets from arrivals list
|
||||
arrival_time_p = 0
|
||||
arrival_time_s = 0
|
||||
estFirstP = 0
|
||||
estFirstS = 0
|
||||
if len(phases['P']) > 0:
|
||||
arrP, arrival_time_p = min([(arr, arr.time) for arr in phases['P']], key=lambda t: t[1])
|
||||
arrP, estFirstP = min([(arr, arr.time) for arr in phases['P']], key=lambda t: t[1])
|
||||
if len(phases['S']) > 0:
|
||||
arrS, arrival_time_s = min([(arr, arr.time) for arr in phases['S']], key=lambda t: t[1])
|
||||
arrS, estFirstS = min([(arr, arr.time) for arr in phases['S']], key=lambda t: t[1])
|
||||
print('autopick: estimated first arrivals for P: {} s, S:{} s after event'
|
||||
' origin time using TauPy'.format(arrival_time_p, arrival_time_s))
|
||||
return arrival_time_p, arrival_time_s
|
||||
' origin time using TauPy'.format(estFirstP, estFirstS))
|
||||
return estFirstP, estFirstS
|
||||
|
||||
def exit_taupy():
|
||||
"""If taupy failed to calculate theoretical starttimes, picking continues.
|
||||
@@ -481,13 +477,10 @@ class AutopickStation(object):
|
||||
raise AttributeError('No source origins given!')
|
||||
|
||||
arrivals = create_arrivals(self.metadata, self.origin, self.pickparams["taup_model"])
|
||||
arrival_P, arrival_S = first_PS_onsets(arrivals)
|
||||
|
||||
self.estFirstP = (self.origin[0].time + arrival_P) - self.ztrace.stats.starttime
|
||||
|
||||
estFirstP, estFirstS = first_PS_onsets(arrivals)
|
||||
# modifiy pstart and pstop relative to estimated first P arrival (relative to station time axis)
|
||||
self.pickparams["pstart"] += self.estFirstP
|
||||
self.pickparams["pstop"] += self.estFirstP
|
||||
self.pickparams["pstart"] += (self.origin[0].time + estFirstP) - self.ztrace.stats.starttime
|
||||
self.pickparams["pstop"] += (self.origin[0].time + estFirstP) - self.ztrace.stats.starttime
|
||||
print('autopick: CF calculation times respectively:'
|
||||
' pstart: {} s, pstop: {} s'.format(self.pickparams["pstart"], self.pickparams["pstop"]))
|
||||
# make sure pstart and pstop are inside the starttime/endtime of vertical trace
|
||||
@@ -498,10 +491,9 @@ class AutopickStation(object):
|
||||
# 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])
|
||||
self.estFirstS = (self.origin[0].time + arrival_S) - trace_s_start
|
||||
# modifiy sstart and sstop relative to estimated first S arrival (relative to station time axis)
|
||||
self.pickparams["sstart"] += self.estFirstS
|
||||
self.pickparams["sstop"] += self.estFirstS
|
||||
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
|
||||
@@ -617,12 +609,6 @@ class AutopickStation(object):
|
||||
# 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')
|
||||
# plot pickwindows for P
|
||||
pstart, pstop = self.pickparams['pstart'], self.pickparams['pstop']
|
||||
if pstart is not None and pstop is not None:
|
||||
ax1.axvspan(pstart, pstop, color='r', alpha=0.1, zorder=0, label='P window')
|
||||
if self.estFirstP is not None:
|
||||
ax1.axvline(self.estFirstP, ls='dashed', color='r', alpha=0.4, label='TauPy estimate')
|
||||
if self.p_results.weight < 4:
|
||||
# plot CF of initial onset (HOScf or ARZcf)
|
||||
ax1.plot(self.cf1.getTimeArray(), self.cf1.getCF() / max(self.cf1.getCF()), 'b', label='CF1')
|
||||
@@ -727,15 +713,6 @@ class AutopickStation(object):
|
||||
ax3.plot([refSpick.getpick() - 0.5, refSpick.getpick() + 0.5], [-1.3, -1.3], 'g', linewidth=2)
|
||||
ax3.plot([self.s_results.lpp, self.s_results.lpp], [-1.1, 1.1], 'g--', label='lpp')
|
||||
ax3.plot([self.s_results.epp, self.s_results.epp], [-1.1, 1.1], 'g--', label='epp')
|
||||
|
||||
# plot pickwindows for S
|
||||
sstart, sstop = self.pickparams['sstart'], self.pickparams['sstop']
|
||||
if sstart is not None and sstop is not None:
|
||||
for axis in [ax2, ax3]:
|
||||
axis.axvspan(sstart, sstop, color='b', alpha=0.1, zorder=0, label='S window')
|
||||
if self.estFirstS is not None:
|
||||
axis.axvline(self.estFirstS, ls='dashed', color='b', alpha=0.4, label='TauPy estimate')
|
||||
|
||||
ax3.legend(loc=1)
|
||||
ax3.set_yticks([])
|
||||
ax3.set_ylim([-1.5, 1.5])
|
||||
@@ -858,21 +835,14 @@ class AutopickStation(object):
|
||||
self.cf1 = None
|
||||
assert isinstance(self.cf1, CharacteristicFunction), 'cf1 is not set correctly: maybe the algorithm name ({})' \
|
||||
' is corrupted'.format(self.pickparams["algoP"])
|
||||
# get the original waveform stream from first CF class cut to identical length as CF for plotting
|
||||
cut_ogstream = self.cf1.getDataArray(self.cf1.getCut())
|
||||
|
||||
# MP: Rename to cf_stream for further use of z_copy and to prevent chaos when z_copy suddenly becomes a cf
|
||||
# stream and later again a waveform stream
|
||||
cf_stream = z_copy.copy()
|
||||
cf_stream[0].data = self.cf1.getCF()
|
||||
|
||||
# calculate AIC cf from first cf (either HOS or ARZ)
|
||||
aiccf = AICcf(cf_stream, cuttimes)
|
||||
z_copy[0].data = self.cf1.getCF()
|
||||
aiccf = AICcf(z_copy, cuttimes)
|
||||
# get preliminary onset time from AIC-CF
|
||||
self.set_current_figure('aicFig')
|
||||
aicpick = AICPicker(aiccf, self.pickparams["tsnrz"], self.pickparams["pickwinP"], self.iplot,
|
||||
Tsmooth=self.pickparams["aictsmooth"], fig=self.current_figure,
|
||||
linecolor=self.current_linecolor, ogstream=cut_ogstream)
|
||||
linecolor=self.current_linecolor)
|
||||
# save aicpick for plotting later
|
||||
self.p_data.aicpick = aicpick
|
||||
# add pstart and pstop to aic plot
|
||||
@@ -885,7 +855,7 @@ class AutopickStation(object):
|
||||
label='P stop')
|
||||
ax.legend(loc=1)
|
||||
|
||||
Pflag = self._pick_p_quality_control(aicpick, cf_stream, tr_filt)
|
||||
Pflag = self._pick_p_quality_control(aicpick, z_copy, tr_filt)
|
||||
# go on with processing if AIC onset passes quality control
|
||||
slope = aicpick.getSlope()
|
||||
if not slope: slope = 0
|
||||
@@ -924,7 +894,7 @@ class AutopickStation(object):
|
||||
refPpick = PragPicker(self.cf2, self.pickparams["tsnrz"], self.pickparams["pickwinP"], self.iplot,
|
||||
self.pickparams["ausP"],
|
||||
self.pickparams["tsmoothP"], aicpick.getpick(), self.current_figure,
|
||||
self.current_linecolor, ogstream=cut_ogstream)
|
||||
self.current_linecolor)
|
||||
# save PragPicker result for plotting
|
||||
self.p_data.refPpick = refPpick
|
||||
self.p_results.mpp = refPpick.getpick()
|
||||
@@ -1176,14 +1146,11 @@ class AutopickStation(object):
|
||||
# calculate AIC cf
|
||||
haiccf = self._calculate_aic_cf_s_pick(cuttimesh)
|
||||
|
||||
# get the original waveform stream cut to identical length as CF for plotting
|
||||
ogstream = haiccf.getDataArray(haiccf.getCut())
|
||||
|
||||
# get preliminary onset time from AIC cf
|
||||
self.set_current_figure('aicARHfig')
|
||||
aicarhpick = AICPicker(haiccf, self.pickparams["tsnrh"], self.pickparams["pickwinS"], self.iplot,
|
||||
Tsmooth=self.pickparams["aictsmoothS"], fig=self.current_figure,
|
||||
linecolor=self.current_linecolor, ogstream=ogstream)
|
||||
linecolor=self.current_linecolor)
|
||||
# save pick for later plotting
|
||||
self.aicarhpick = aicarhpick
|
||||
|
||||
|
||||
+43
-15
@@ -17,11 +17,7 @@ autoregressive prediction: application ot local and regional distances, Geophys.
|
||||
:author: MAGS2 EP3 working group
|
||||
"""
|
||||
import numpy as np
|
||||
try:
|
||||
from scipy.signal import tukey
|
||||
except ImportError:
|
||||
from scipy.signal.windows import tukey
|
||||
|
||||
from scipy import signal
|
||||
from obspy.core import Stream
|
||||
|
||||
from pylot.core.pick.utils import PickingFailedException
|
||||
@@ -60,7 +56,7 @@ class CharacteristicFunction(object):
|
||||
self.setOrder(order)
|
||||
self.setFnoise(fnoise)
|
||||
self.setARdetStep(t2)
|
||||
self.calcCF()
|
||||
self.calcCF(self.getDataArray())
|
||||
self.arpara = np.array([])
|
||||
self.xpred = np.array([])
|
||||
|
||||
@@ -212,15 +208,17 @@ class CharacteristicFunction(object):
|
||||
data = self.orig_data.copy()
|
||||
return data
|
||||
|
||||
def calcCF(self):
|
||||
pass
|
||||
def calcCF(self, data=None):
|
||||
self.cf = data
|
||||
|
||||
|
||||
class AICcf(CharacteristicFunction):
|
||||
|
||||
def calcCF(self):
|
||||
def calcCF(self, data):
|
||||
"""
|
||||
Function to calculate the Akaike Information Criterion (AIC) after Maeda (1985).
|
||||
:param data: data, time series (whether seismogram or CF)
|
||||
:type data: tuple
|
||||
:return: AIC function
|
||||
:rtype:
|
||||
"""
|
||||
@@ -229,7 +227,7 @@ class AICcf(CharacteristicFunction):
|
||||
ind = np.where(~np.isnan(xnp))[0]
|
||||
if ind.size:
|
||||
xnp[:ind[0]] = xnp[ind[0]]
|
||||
xnp = tukey(len(xnp), alpha=0.05) * xnp
|
||||
xnp = signal.tukey(len(xnp), alpha=0.05) * xnp
|
||||
xnp = xnp - np.mean(xnp)
|
||||
datlen = len(xnp)
|
||||
k = np.arange(1, datlen)
|
||||
@@ -258,11 +256,13 @@ class HOScf(CharacteristicFunction):
|
||||
"""
|
||||
super(HOScf, self).__init__(data, cut, pickparams["tlta"], pickparams["hosorder"])
|
||||
|
||||
def calcCF(self):
|
||||
def calcCF(self, data):
|
||||
"""
|
||||
Function to calculate skewness (statistics of order 3) or kurtosis
|
||||
(statistics of order 4), using one long moving window, as published
|
||||
in Kueperkoch et al. (2010), or order 2, i.e. STA/LTA.
|
||||
:param data: data, time series (whether seismogram or CF)
|
||||
:type data: tuple
|
||||
:return: HOS cf
|
||||
:rtype:
|
||||
"""
|
||||
@@ -277,28 +277,47 @@ class HOScf(CharacteristicFunction):
|
||||
elif self.getOrder() == 4: # this is kurtosis
|
||||
y = np.power(xnp, 4)
|
||||
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
|
||||
# t2: long term moving window
|
||||
ilta = int(round(self.getTime2() / self.getIncrement()))
|
||||
ista = int(round((self.getTime2() / 10) / self.getIncrement())) # TODO: still hard coded!!
|
||||
lta = y[0]
|
||||
lta1 = y1[0]
|
||||
sta = y[0]
|
||||
# moving windows
|
||||
LTA = np.zeros(len(xnp))
|
||||
STA = np.zeros(len(xnp))
|
||||
for j in range(0, len(xnp)):
|
||||
if j < 4:
|
||||
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:
|
||||
lta = (y[j] + lta * (j - 1)) / j
|
||||
lta1 = (y1[j] + lta1 * (j - 1)) / j
|
||||
if self.getOrder() == 2:
|
||||
sta = (y[j] - y[j - ista]) / ista + sta
|
||||
else:
|
||||
lta = (y[j] - y[j - ilta]) / ilta + lta
|
||||
lta1 = (y1[j] - y1[j - ilta]) / ilta + lta1
|
||||
if self.getOrder() == 2:
|
||||
sta = (y[j] - y[j - ista]) / ista + sta
|
||||
# define LTA
|
||||
if self.getOrder() == 3:
|
||||
LTA[j] = lta / np.power(lta1, 1.5)
|
||||
elif self.getOrder() == 4:
|
||||
LTA[j] = lta / np.power(lta1, 2)
|
||||
else:
|
||||
LTA[j] = lta
|
||||
STA[j] = sta
|
||||
|
||||
# remove NaN's with first not-NaN-value,
|
||||
# so autopicker doesnt pick discontinuity at start of the trace
|
||||
@@ -307,7 +326,10 @@ class HOScf(CharacteristicFunction):
|
||||
first = ind[0]
|
||||
LTA[:first] = LTA[first]
|
||||
|
||||
self.cf = LTA
|
||||
if self.getOrder() > 2:
|
||||
self.cf = LTA
|
||||
else: # order 2 means STA/LTA!
|
||||
self.cf = STA / LTA
|
||||
self.xcf = x
|
||||
|
||||
|
||||
@@ -317,10 +339,12 @@ class ARZcf(CharacteristicFunction):
|
||||
super(ARZcf, self).__init__(data, cut, t1=t1, t2=t2, order=pickparams["Parorder"],
|
||||
fnoise=pickparams["addnoise"])
|
||||
|
||||
def calcCF(self):
|
||||
def calcCF(self, data):
|
||||
"""
|
||||
function used to calculate the AR prediction error from a single vertical trace. Can be used to pick
|
||||
P onsets.
|
||||
:param data:
|
||||
:type data: ~obspy.core.stream.Stream
|
||||
:return: ARZ cf
|
||||
:rtype:
|
||||
"""
|
||||
@@ -451,12 +475,14 @@ class ARHcf(CharacteristicFunction):
|
||||
super(ARHcf, self).__init__(data, cut, t1=t1, t2=t2, order=pickparams["Sarorder"],
|
||||
fnoise=pickparams["addnoise"])
|
||||
|
||||
def calcCF(self):
|
||||
def calcCF(self, data):
|
||||
"""
|
||||
Function to calculate a characteristic function using autoregressive modelling of the waveform of
|
||||
both horizontal traces.
|
||||
The waveform is predicted in a moving time window using the calculated AR parameters. The difference
|
||||
between the predicted and the actual waveform servers as a characteristic function.
|
||||
:param data: wavefor stream
|
||||
:type data: ~obspy.core.stream.Stream
|
||||
:return: ARH cf
|
||||
:rtype:
|
||||
"""
|
||||
@@ -605,12 +631,14 @@ class AR3Ccf(CharacteristicFunction):
|
||||
super(AR3Ccf, self).__init__(data, cut, t1=t1, t2=t2, order=pickparams["Sarorder"],
|
||||
fnoise=pickparams["addnoise"])
|
||||
|
||||
def calcCF(self):
|
||||
def calcCF(self, data):
|
||||
"""
|
||||
Function to calculate a characteristic function using autoregressive modelling of the waveform of
|
||||
all three traces.
|
||||
The waveform is predicted in a moving time window using the calculated AR parameters. The difference
|
||||
between the predicted and the actual waveform servers as a characteristic function
|
||||
:param data: stream holding all three traces
|
||||
:type data: ~obspy.core.stream.Stream
|
||||
:return: AR3C cf
|
||||
:rtype:
|
||||
"""
|
||||
|
||||
+14
-15
@@ -37,8 +37,7 @@ class AutoPicker(object):
|
||||
|
||||
warnings.simplefilter('ignore')
|
||||
|
||||
def __init__(self, cf, TSNR, PickWindow, iplot=0, aus=None, Tsmooth=None, Pick1=None,
|
||||
fig=None, linecolor='k', ogstream=None):
|
||||
def __init__(self, cf, TSNR, PickWindow, iplot=0, aus=None, Tsmooth=None, Pick1=None, fig=None, linecolor='k'):
|
||||
"""
|
||||
Create AutoPicker object
|
||||
:param cf: characteristic function, on which the picking algorithm is applied
|
||||
@@ -60,15 +59,12 @@ class AutoPicker(object):
|
||||
:type fig: `~matplotlib.figure.Figure`
|
||||
:param linecolor: matplotlib line color string
|
||||
:type linecolor: str
|
||||
:param ogstream: original stream (waveform), e.g. for plotting purposes
|
||||
:type ogstream: `~obspy.core.stream.Stream`
|
||||
"""
|
||||
|
||||
assert isinstance(cf, CharacteristicFunction), "%s is not a CharacteristicFunction object" % str(cf)
|
||||
self._linecolor = linecolor
|
||||
self._pickcolor_p = 'b'
|
||||
self.cf = cf.getCF()
|
||||
self.ogstream = ogstream
|
||||
self.Tcf = cf.getTimeArray()
|
||||
self.Data = cf.getXCF()
|
||||
self.dt = cf.getIncrement()
|
||||
@@ -177,7 +173,7 @@ class AICPicker(AutoPicker):
|
||||
nn = np.isnan(self.cf)
|
||||
if len(nn) > 1:
|
||||
self.cf[nn] = 0
|
||||
# taper AIC-CF to get rid of side maxima
|
||||
# taper AIC-CF to get rid off side maxima
|
||||
tap = np.hanning(len(self.cf))
|
||||
aic = tap * self.cf + max(abs(self.cf))
|
||||
# smooth AIC-CF
|
||||
@@ -320,7 +316,16 @@ class AICPicker(AutoPicker):
|
||||
plt.close(fig)
|
||||
return
|
||||
iislope = islope[0][0:imax + 1]
|
||||
dataslope = self.Data[0].data[iislope]
|
||||
# MP MP change slope calculation
|
||||
# get all maxima of aicsmooth
|
||||
iaicmaxima = argrelmax(aicsmooth)[0]
|
||||
# get first index of maximum after pickindex (indices saved in iaicmaxima)
|
||||
aicmax = iaicmaxima[np.where(iaicmaxima > pickindex)[0]]
|
||||
if len(aicmax) > 0:
|
||||
iaicmax = aicmax[0]
|
||||
else:
|
||||
iaicmax = -1
|
||||
dataslope = aicsmooth[pickindex: iaicmax]
|
||||
# calculate slope as polynomal fit of order 1
|
||||
xslope = np.arange(0, len(dataslope), 1)
|
||||
try:
|
||||
@@ -331,7 +336,7 @@ class AICPicker(AutoPicker):
|
||||
else:
|
||||
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
|
||||
self.slope /= self.Data[0].data[icfmax]
|
||||
self.slope /= aicsmooth[iaicmax]
|
||||
except Exception as e:
|
||||
print("AICPicker: Problems with data fitting! {}".format(e))
|
||||
|
||||
@@ -351,12 +356,6 @@ class AICPicker(AutoPicker):
|
||||
self.Tcf = self.Tcf[0:len(self.Tcf) - 1]
|
||||
ax1.plot(self.Tcf, cf / max(cf), color=self._linecolor, linewidth=0.7, label='(HOS-/AR-) Data')
|
||||
ax1.plot(self.Tcf, aicsmooth / max(aicsmooth), 'r', label='Smoothed AIC-CF')
|
||||
# plot the original waveform also for evaluation of the CF and pick
|
||||
if self.ogstream:
|
||||
data = self.ogstream[0].data
|
||||
if len(data) == len(self.Tcf):
|
||||
ax1.plot(self.Tcf, 0.5 * data / max(data), 'k', label='Seismogram', alpha=0.3, zorder=0,
|
||||
lw=0.5)
|
||||
if self.Pick is not None:
|
||||
ax1.plot([self.Pick, self.Pick], [-0.1, 0.5], 'b', linewidth=2, label='AIC-Pick')
|
||||
ax1.set_xlabel('Time [s] since %s' % self.Data[0].stats.starttime)
|
||||
@@ -377,7 +376,7 @@ class AICPicker(AutoPicker):
|
||||
label='Signal Window')
|
||||
ax2.axvspan(self.Tcf[iislope[0]], self.Tcf[iislope[-1]], color='g', alpha=0.2, lw=0,
|
||||
label='Slope Window')
|
||||
ax2.plot(self.Tcf[iislope], datafit, 'g', linewidth=2,
|
||||
ax2.plot(self.Tcf[pickindex: iaicmax], datafit, 'g', linewidth=2,
|
||||
label='Slope') # MP MP changed temporarily!
|
||||
|
||||
if self.slope is not None:
|
||||
|
||||
@@ -15,7 +15,7 @@ import numpy as np
|
||||
from obspy.core import Stream, UTCDateTime
|
||||
from scipy.signal import argrelmax
|
||||
|
||||
from pylot.core.util.utils import get_bool, get_none, SetChannelComponents, common_range
|
||||
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'):
|
||||
@@ -828,22 +828,14 @@ def checksignallength(X, pick, minsiglength, pickparams, iplot=0, fig=None, line
|
||||
if len(X) > 1:
|
||||
# all three components available
|
||||
# make sure, all components have equal lengths
|
||||
earliest_starttime = min(tr.stats.starttime for tr in X)
|
||||
cuttimes = common_range(X)
|
||||
X = X.slice(cuttimes[0], cuttimes[1])
|
||||
x1, x2, x3 = X[:3]
|
||||
|
||||
if not (len(x1) == len(x2) == len(x3)):
|
||||
raise PickingFailedException('checksignallength: unequal lengths of components!')
|
||||
|
||||
ilen = min([len(X[0].data), len(X[1].data), len(X[2].data)])
|
||||
x1 = X[0][0:ilen]
|
||||
x2 = X[1][0:ilen]
|
||||
x3 = X[2][0:ilen]
|
||||
# get RMS trace
|
||||
rms = np.sqrt((np.power(x1, 2) + np.power(x2, 2) + np.power(x3, 2)) / 3)
|
||||
ilen = len(rms)
|
||||
dt = earliest_starttime - X[0].stats.starttime
|
||||
pick -= dt
|
||||
else:
|
||||
x1 = X[0].data
|
||||
x2 = x3 = None
|
||||
ilen = len(x1)
|
||||
rms = abs(x1)
|
||||
|
||||
@@ -882,10 +874,6 @@ def checksignallength(X, pick, minsiglength, pickparams, iplot=0, fig=None, line
|
||||
fig._tight = True
|
||||
ax = fig.add_subplot(111)
|
||||
ax.plot(t, rms, color=linecolor, linewidth=0.7, label='RMS Data')
|
||||
ax.plot(t, x1, 'k', alpha=0.3, lw=0.3, zorder=0)
|
||||
if x2 is not None and x3 is not None:
|
||||
ax.plot(t, x2, 'r', alpha=0.3, lw=0.3, zorder=0)
|
||||
ax.plot(t, x3, 'g', alpha=0.3, lw=0.3, zorder=0)
|
||||
ax.axvspan(t[inoise[0]], t[inoise[-1]], color='y', alpha=0.2, lw=0, label='Noise Window')
|
||||
ax.axvspan(t[isignal[0]], t[isignal[-1]], color='b', alpha=0.2, lw=0, label='Signal Window')
|
||||
ax.plot([t[isignal[0]], t[isignal[len(isignal) - 1]]],
|
||||
@@ -895,7 +883,6 @@ def checksignallength(X, pick, minsiglength, pickparams, iplot=0, fig=None, line
|
||||
ax.set_xlabel('Time [s] since %s' % X[0].stats.starttime)
|
||||
ax.set_ylabel('Counts')
|
||||
ax.set_title('Check for Signal Length, Station %s' % X[0].stats.station)
|
||||
ax.set_xlim(pickparams["pstart"], pickparams["pstop"])
|
||||
ax.set_yticks([])
|
||||
if plt_flag == 1:
|
||||
fig.show()
|
||||
|
||||
+103
-147
@@ -5,17 +5,14 @@ import traceback
|
||||
|
||||
import cartopy.crs as ccrs
|
||||
import cartopy.feature as cf
|
||||
from cartopy.mpl.ticker import LongitudeFormatter, LatitudeFormatter
|
||||
import matplotlib
|
||||
import matplotlib.patheffects as PathEffects
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
import obspy
|
||||
from PySide2 import QtWidgets, QtGui
|
||||
from PySide2 import QtWidgets
|
||||
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
|
||||
from mpl_toolkits.axes_grid1.inset_locator import inset_axes
|
||||
from obspy import UTCDateTime
|
||||
|
||||
from pylot.core.util.utils import identifyPhaseID
|
||||
from scipy.interpolate import griddata
|
||||
|
||||
@@ -27,10 +24,10 @@ matplotlib.use('Qt5Agg')
|
||||
|
||||
class MplCanvas(FigureCanvas):
|
||||
|
||||
def __init__(self, extern_axes=None, projection=None, width=15, height=5, dpi=100):
|
||||
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, projection=projection)
|
||||
self.axes = self.fig.add_subplot(111)
|
||||
else:
|
||||
self.fig = extern_axes.figure
|
||||
self.axes = extern_axes
|
||||
@@ -62,30 +59,24 @@ class Array_map(QtWidgets.QWidget):
|
||||
self.parameter = parameter if parameter else parent._inputs
|
||||
|
||||
self.picks_rel = {}
|
||||
self.picks_rel_mean_corrected = {}
|
||||
self.marked_stations = []
|
||||
self.highlighted_stations = []
|
||||
|
||||
# call functions to draw everything
|
||||
self.projection = ccrs.PlateCarree()
|
||||
self.init_graphics()
|
||||
self.ax = self.canvas.axes
|
||||
self.ax.set_adjustable('datalim')
|
||||
|
||||
self.init_stations()
|
||||
self.init_crtpyMap()
|
||||
self.init_map()
|
||||
|
||||
# set original map limits to fall back on when home button is pressed
|
||||
self.org_xlim = self.ax.get_xlim()
|
||||
self.org_ylim = self.ax.get_ylim()
|
||||
|
||||
self.org_xlim = self.canvas.axes.get_xlim()
|
||||
self.org_ylim = self.canvas.axes.get_ylim()
|
||||
# initial map without event
|
||||
self.ax.set_xlim(self.org_xlim[0], self.org_xlim[1])
|
||||
self.ax.set_ylim(self.org_ylim[0], self.org_ylim[1])
|
||||
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
|
||||
|
||||
|
||||
def init_map(self):
|
||||
self.init_colormap()
|
||||
self.connectSignals()
|
||||
@@ -98,24 +89,23 @@ class Array_map(QtWidgets.QWidget):
|
||||
# initialize figure elements
|
||||
|
||||
if self.extern_plot_axes is None:
|
||||
self.canvas = MplCanvas(projection=self.projection)
|
||||
self.canvas = MplCanvas(self)
|
||||
self.plotWidget = FigureCanvas(self.canvas.fig)
|
||||
else:
|
||||
self.canvas = MplCanvas(extern_axes=self.extern_plot_axes)
|
||||
|
||||
self.plotWidget = self.canvas
|
||||
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.subtract_mean_cb = QtWidgets.QCheckBox('Subtract mean')
|
||||
|
||||
self.main_box = QtWidgets.QVBoxLayout()
|
||||
self.setLayout(self.main_box)
|
||||
|
||||
self.top_row = QtWidgets.QHBoxLayout()
|
||||
self.main_box.addLayout(self.top_row, 0)
|
||||
self.main_box.addLayout(self.top_row, 1)
|
||||
|
||||
self.comboBox_phase = QtWidgets.QComboBox()
|
||||
self.comboBox_phase.insertItem(0, 'P')
|
||||
@@ -134,8 +124,8 @@ class Array_map(QtWidgets.QWidget):
|
||||
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 plasma as default
|
||||
self.cmaps_box.setCurrentIndex(self.cmaps_box.findText('plasma'))
|
||||
# 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)
|
||||
@@ -148,15 +138,14 @@ class Array_map(QtWidgets.QWidget):
|
||||
self.top_row.addWidget(self.auto_refresh_box)
|
||||
self.top_row.addWidget(self.refresh_button)
|
||||
|
||||
self.main_box.addWidget(self.plotWidget, 10)
|
||||
self.main_box.addWidget(self.plotWidget, 1)
|
||||
|
||||
self.bot_row = QtWidgets.QHBoxLayout()
|
||||
self.main_box.addLayout(self.bot_row, 0)
|
||||
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.subtract_mean_cb, 0)
|
||||
self.bot_row.addWidget(self.status_label, 5)
|
||||
|
||||
def init_colormap(self):
|
||||
@@ -164,12 +153,14 @@ class Array_map(QtWidgets.QWidget):
|
||||
self.init_lat_lon_grid()
|
||||
|
||||
def init_crtpyMap(self):
|
||||
self.ax.add_feature(cf.LAND)
|
||||
self.ax.add_feature(cf.OCEAN)
|
||||
self.ax.add_feature(cf.COASTLINE, linewidth=1, edgecolor='gray')
|
||||
self.ax.add_feature(cf.BORDERS, alpha=0.7)
|
||||
self.ax.add_feature(cf.LAKES, alpha=0.7)
|
||||
self.ax.add_feature(cf.RIVERS, linewidth=1)
|
||||
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()
|
||||
@@ -177,8 +168,12 @@ class Array_map(QtWidgets.QWidget):
|
||||
self.canvas.fig.tight_layout()
|
||||
|
||||
def add_merid_paral(self):
|
||||
self.gridlines = self.ax.gridlines(draw_labels=False, alpha=0.6, color='gray',
|
||||
linewidth=self.linewidth / 2, zorder=7, crs=ccrs.PlateCarree())
|
||||
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):
|
||||
@@ -186,24 +181,24 @@ class Array_map(QtWidgets.QWidget):
|
||||
self.gridlines.yline_artists[0].remove()
|
||||
|
||||
def org_map_view(self):
|
||||
self.ax.set_xlim(self.org_xlim[0], self.org_xlim[1])
|
||||
self.ax.set_ylim(self.org_ylim[0], self.org_ylim[1])
|
||||
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.remove_merid_paral()
|
||||
self.add_merid_paral()
|
||||
|
||||
self.canvas.draw_idle()
|
||||
self.canvas.axes.figure.canvas.draw_idle()
|
||||
|
||||
def go2eq(self):
|
||||
if self.eventLoc:
|
||||
lats, lons = self.eventLoc
|
||||
self.ax.set_xlim(lons - 10, lons + 10)
|
||||
self.ax.set_ylim(lats - 5, lats + 5)
|
||||
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.remove_merid_paral()
|
||||
self.add_merid_paral()
|
||||
|
||||
self.canvas.draw_idle()
|
||||
self.canvas.axes.figure.canvas.draw_idle()
|
||||
|
||||
else:
|
||||
self.status_label.setText('No event information available')
|
||||
@@ -217,7 +212,6 @@ class Array_map(QtWidgets.QWidget):
|
||||
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.subtract_mean_cb.stateChanged.connect(self.toggle_subtract_mean)
|
||||
|
||||
self.plotWidget.mpl_connect('motion_notify_event', self.mouse_moved)
|
||||
self.plotWidget.mpl_connect('scroll_event', self.mouse_scroll)
|
||||
@@ -226,32 +220,21 @@ class Array_map(QtWidgets.QWidget):
|
||||
|
||||
# set mouse events -----------------------------------------------------
|
||||
def mouse_moved(self, event):
|
||||
if not event.inaxes == self.ax:
|
||||
if not event.inaxes == self.canvas.axes:
|
||||
return
|
||||
else:
|
||||
cont, inds = self.sc.contains(event)
|
||||
lat = event.ydata
|
||||
lon = event.xdata
|
||||
text = f'Longitude: {lon:3.3f}, Latitude: {lat:3.3f}'
|
||||
|
||||
if cont:
|
||||
indices = inds['ind']
|
||||
text += ' | Station: ' if len(indices) == 1 else ' | Stations: '
|
||||
text += ' - '.join([self._station_onpick_ids[index] for index in indices[:5]])
|
||||
if len(indices) > 5:
|
||||
text += '...'
|
||||
|
||||
self.status_label.setText(text)
|
||||
self.status_label.setText('Latitude: {:3.5f}, Longitude: {:3.5f}'.format(lat, lon))
|
||||
|
||||
def mouse_scroll(self, event):
|
||||
if not event.inaxes == self.ax:
|
||||
if not event.inaxes == self.canvas.axes:
|
||||
return
|
||||
|
||||
zoom = {'up': 1. / 2., 'down': 2.}
|
||||
|
||||
if event.button in zoom:
|
||||
xlim = self.ax.get_xlim()
|
||||
ylim = self.ax.get_ylim()
|
||||
xlim = self.canvas.axes.get_xlim()
|
||||
ylim = self.canvas.axes.get_ylim()
|
||||
|
||||
x, y = event.xdata, event.ydata
|
||||
|
||||
@@ -263,24 +246,24 @@ class Array_map(QtWidgets.QWidget):
|
||||
yb = y - 0.5 * ydiff
|
||||
yt = y + 0.5 * ydiff
|
||||
|
||||
self.ax.set_xlim(xl, xr)
|
||||
self.ax.set_ylim(yb, yt)
|
||||
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.remove_merid_paral()
|
||||
self.add_merid_paral()
|
||||
|
||||
self.ax.figure.canvas.draw_idle()
|
||||
self.canvas.axes.figure.canvas.draw_idle()
|
||||
|
||||
def mouseLeftPress(self, event):
|
||||
if not event.inaxes == self.ax:
|
||||
if not event.inaxes == self.canvas.axes:
|
||||
return
|
||||
self.map_x = event.xdata
|
||||
self.map_y = event.ydata
|
||||
self.map_xlim = self.ax.get_xlim()
|
||||
self.map_ylim = self.ax.get_ylim()
|
||||
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.ax:
|
||||
if not event.inaxes == self.canvas.axes:
|
||||
return
|
||||
new_x = event.xdata
|
||||
new_y = event.ydata
|
||||
@@ -288,13 +271,13 @@ class Array_map(QtWidgets.QWidget):
|
||||
dx = new_x - self.map_x
|
||||
dy = new_y - self.map_y
|
||||
|
||||
self.ax.set_xlim((self.map_xlim[0] - dx, self.map_xlim[1] - dx))
|
||||
self.ax.set_ylim(self.map_ylim[0] - dy, self.map_ylim[1] - dy)
|
||||
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.remove_merid_paral()
|
||||
self.add_merid_paral()
|
||||
|
||||
self.ax.figure.canvas.draw_idle()
|
||||
self.canvas.axes.figure.canvas.draw_idle()
|
||||
|
||||
def onpick(self, event):
|
||||
btn_msg = {1: ' in selection. Aborted', 2: ' to delete a pick on. Aborted', 3: ' to display info.'}
|
||||
@@ -374,6 +357,12 @@ class Array_map(QtWidgets.QWidget):
|
||||
def get_max_from_stations(self, key):
|
||||
return self._from_dict(max, key)
|
||||
|
||||
def get_min_from_picks(self):
|
||||
return min(self.picks_rel.values())
|
||||
|
||||
def get_max_from_picks(self):
|
||||
return max(self.picks_rel.values())
|
||||
|
||||
def current_picks_dict(self):
|
||||
picktype = self.comboBox_am.currentText().split(' ')[0]
|
||||
auto_manu = {'auto': self.autopicks_dict,
|
||||
@@ -418,34 +407,22 @@ class Array_map(QtWidgets.QWidget):
|
||||
print('Cannot display pick for station {}. Reason: {}'.format(station_name, e))
|
||||
return picks, uncertainties
|
||||
|
||||
def get_picks_rel(picks, func=min):
|
||||
def get_picks_rel(picks):
|
||||
picks_rel = {}
|
||||
picks_utc = []
|
||||
for pick in picks.values():
|
||||
if type(pick) is UTCDateTime:
|
||||
picks_utc.append(pick.timestamp)
|
||||
if type(pick) is obspy.core.utcdatetime.UTCDateTime:
|
||||
picks_utc.append(pick)
|
||||
if picks_utc:
|
||||
self._reference_picktime = UTCDateTime(func(picks_utc))
|
||||
self._earliest_picktime = min(picks_utc)
|
||||
for st_id, pick in picks.items():
|
||||
if type(pick) is UTCDateTime:
|
||||
pick -= self._reference_picktime
|
||||
if type(pick) is obspy.core.utcdatetime.UTCDateTime:
|
||||
pick -= self._earliest_picktime
|
||||
picks_rel[st_id] = pick
|
||||
return picks_rel
|
||||
|
||||
def get_picks_rel_mean_corr(picks):
|
||||
return get_picks_rel(picks, func=np.nanmean)
|
||||
|
||||
self.picks, self.uncertainties = get_picks(self.stations_dict)
|
||||
self.picks_rel = get_picks_rel(self.picks)
|
||||
self.picks_rel_mean_corrected = get_picks_rel_mean_corr(self.picks)
|
||||
|
||||
def toggle_subtract_mean(self):
|
||||
if self.subtract_mean_cb.isChecked():
|
||||
cmap = 'seismic'
|
||||
else:
|
||||
cmap = 'viridis'
|
||||
self.cmaps_box.setCurrentIndex(self.cmaps_box.findText(cmap))
|
||||
self._refresh_drawings()
|
||||
|
||||
def init_lat_lon_dimensions(self):
|
||||
# init minimum and maximum lon and lat dimensions
|
||||
@@ -476,12 +453,11 @@ class Array_map(QtWidgets.QWidget):
|
||||
return stations, latitudes, longitudes
|
||||
|
||||
def get_picks_lat_lon(self):
|
||||
picks_rel = self.picks_rel_mean_corrected if self.subtract_mean_cb.isChecked() else self.picks_rel
|
||||
picks = []
|
||||
uncertainties = []
|
||||
latitudes = []
|
||||
longitudes = []
|
||||
for st_id, pick in picks_rel.items():
|
||||
for st_id, pick in self.picks_rel.items():
|
||||
picks.append(pick)
|
||||
uncertainties.append(self.uncertainties.get(st_id))
|
||||
latitudes.append(self.stations_dict[st_id]['latitude'])
|
||||
@@ -493,20 +469,13 @@ class Array_map(QtWidgets.QWidget):
|
||||
stat_dict = self.stations_dict['{}.{}'.format(network, station)]
|
||||
lat = stat_dict['latitude']
|
||||
lon = stat_dict['longitude']
|
||||
self.highlighted_stations.append(self.ax.scatter(lon, lat, s=self.pointsize, edgecolors=color,
|
||||
facecolors='none', zorder=12,
|
||||
transform=ccrs.PlateCarree(), label='deleted'))
|
||||
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):
|
||||
try:
|
||||
wfdata = self._parent.get_data().getWFData()
|
||||
except AttributeError:
|
||||
QtWidgets.QMessageBox.warning(
|
||||
self, "PyLoT Warning",
|
||||
"No waveform data found. Check if they were already loaded in Waveform plot tab."
|
||||
)
|
||||
return
|
||||
wfdata_comp = self._parent.get_data().getAltWFdata()
|
||||
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
|
||||
@@ -521,6 +490,7 @@ class Array_map(QtWidgets.QWidget):
|
||||
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)
|
||||
@@ -548,19 +518,12 @@ class Array_map(QtWidgets.QWidget):
|
||||
print(message, e)
|
||||
print(traceback.format_exc())
|
||||
|
||||
def draw_contour_filled(self, nlevel=51):
|
||||
if self.subtract_mean_cb.isChecked():
|
||||
abs_max = self.get_residuals_absmax()
|
||||
levels = np.linspace(-abs_max, abs_max, nlevel)
|
||||
else:
|
||||
levels = np.linspace(min(self.picks_rel.values()), max(self.picks_rel.values()), nlevel)
|
||||
def draw_contour_filled(self, nlevel=50):
|
||||
levels = np.linspace(self.get_min_from_picks(), self.get_max_from_picks(), nlevel)
|
||||
|
||||
self.contourf = self.ax.contourf(self.longrid, self.latgrid, self.picksgrid_active, levels,
|
||||
linewidths=self.linewidth * 5, transform=ccrs.PlateCarree(),
|
||||
alpha=0.4, zorder=8, cmap=self.get_colormap())
|
||||
|
||||
def get_residuals_absmax(self):
|
||||
return np.max(np.absolute(list(self.picks_rel_mean_corrected.values())))
|
||||
self.contourf = self.canvas.axes.contourf(self.longrid, self.latgrid, self.picksgrid_active, levels,
|
||||
linewidths=self.linewidth * 5, transform=ccrs.PlateCarree(),
|
||||
alpha=0.4, zorder=8, cmap=self.get_colormap())
|
||||
|
||||
def get_colormap(self):
|
||||
return plt.get_cmap(self.cmaps_box.currentText())
|
||||
@@ -568,18 +531,18 @@ class Array_map(QtWidgets.QWidget):
|
||||
def scatter_all_stations(self):
|
||||
stations, lats, lons = self.get_st_lat_lon_for_plot()
|
||||
|
||||
self.sc = self.ax.scatter(lons, lats, s=self.pointsize * 3, facecolor='none', marker='.',
|
||||
zorder=10, picker=True, edgecolor='0.5', label='Not Picked',
|
||||
transform=ccrs.PlateCarree())
|
||||
self.sc = self.canvas.axes.scatter(lons, lats, s=self.pointsize * 3, facecolor='none', marker='.',
|
||||
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
|
||||
if self.eventLoc:
|
||||
lats, lons = self.eventLoc
|
||||
self.sc_event = self.ax.scatter(lons, lats, s=5 * self.pointsize, facecolor='red', zorder=11,
|
||||
label='Event (might be outside map region)', marker='*',
|
||||
edgecolors='black',
|
||||
transform=ccrs.PlateCarree())
|
||||
self.sc_event = self.canvas.axes.scatter(lons, lats, s=5 * self.pointsize, facecolor='red', zorder=11,
|
||||
label='Event (might be outside map region)', marker='*',
|
||||
edgecolors='black',
|
||||
transform=ccrs.PlateCarree())
|
||||
|
||||
def scatter_picked_stations(self):
|
||||
picks, uncertainties, lats, lons = self.get_picks_lat_lon()
|
||||
@@ -592,13 +555,8 @@ class Array_map(QtWidgets.QWidget):
|
||||
for uncertainty in uncertainties])
|
||||
|
||||
cmap = self.get_colormap()
|
||||
|
||||
vmin = vmax = None
|
||||
if self.subtract_mean_cb.isChecked():
|
||||
vmin, vmax = -self.get_residuals_absmax(), self.get_residuals_absmax()
|
||||
|
||||
self.sc_picked = self.ax.scatter(lons, lats, s=sizes, edgecolors='white', cmap=cmap, vmin=vmin, vmax=vmax,
|
||||
c=picks, zorder=11, label='Picked', transform=ccrs.PlateCarree())
|
||||
self.sc_picked = self.canvas.axes.scatter(lons, lats, s=sizes, edgecolors='white', cmap=cmap,
|
||||
c=picks, zorder=11, label='Picked', transform=ccrs.PlateCarree())
|
||||
|
||||
def annotate_ax(self):
|
||||
self.annotations = []
|
||||
@@ -616,20 +574,20 @@ class Array_map(QtWidgets.QWidget):
|
||||
if st in self.marked_stations:
|
||||
color = 'red'
|
||||
self.annotations.append(
|
||||
self.ax.annotate(f'{st}', xy=(x + 0.003, y + 0.003), fontsize=self.pointsize / 4.,
|
||||
fontweight='semibold', color=color, alpha=0.8,
|
||||
transform=ccrs.PlateCarree(), zorder=14,
|
||||
path_effects=[PathEffects.withStroke(
|
||||
linewidth=self.pointsize / 15., foreground='k')]))
|
||||
self.canvas.axes.annotate(' %s' % st, xy=(x + 0.003, y + 0.003), fontsize=self.pointsize / 4.,
|
||||
fontweight='semibold', color=color, alpha=0.8,
|
||||
transform=ccrs.PlateCarree(), zorder=14,
|
||||
path_effects=[PathEffects.withStroke(
|
||||
linewidth=self.pointsize / 15., foreground='k')]))
|
||||
|
||||
self.legend = self.ax.legend(loc=1, framealpha=1)
|
||||
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):
|
||||
self.cbax_bg = inset_axes(self.ax, width="6%", height="75%", loc=5)
|
||||
cbax = inset_axes(self.ax, width='2%', height='70%', loc=5)
|
||||
cbar = self.ax.figure.colorbar(self.sc_picked, cax=cbax)
|
||||
self.cbax_bg = inset_axes(self.canvas.axes, width="6%", height="75%", loc=5)
|
||||
cbax = inset_axes(self.canvas.axes, width='2%', height='70%', loc=5)
|
||||
cbar = self.canvas.axes.figure.colorbar(self.sc_picked, cax=cbax)
|
||||
cbar.set_label(label)
|
||||
cbax.yaxis.tick_left()
|
||||
cbax.yaxis.set_label_position('left')
|
||||
@@ -674,9 +632,7 @@ class Array_map(QtWidgets.QWidget):
|
||||
if picks_available:
|
||||
self.scatter_picked_stations()
|
||||
if hasattr(self, 'sc_picked'):
|
||||
self.cbar = self.add_cbar(
|
||||
label='Time relative to reference onset ({}) [s]'.format(self._reference_picktime)
|
||||
)
|
||||
self.cbar = self.add_cbar(label='Time relative to first onset ({}) [s]'.format(self._earliest_picktime))
|
||||
self.comboBox_phase.setEnabled(True)
|
||||
else:
|
||||
self.comboBox_phase.setEnabled(False)
|
||||
|
||||
@@ -27,10 +27,6 @@ class Metadata(object):
|
||||
# saves which metadata files are from obspy dmt
|
||||
self.obspy_dmt_invs = []
|
||||
if inventory:
|
||||
# make sure that no accidental backslashes mess up the path
|
||||
if isinstance(inventory, str):
|
||||
inventory = inventory.replace('\\', '/')
|
||||
inventory = os.path.abspath(inventory)
|
||||
if os.path.isdir(inventory):
|
||||
self.add_inventory(inventory)
|
||||
if os.path.isfile(inventory):
|
||||
@@ -59,8 +55,6 @@ class Metadata(object):
|
||||
:type path_to_inventory: str
|
||||
:return: None
|
||||
"""
|
||||
path_to_inventory = path_to_inventory.replace('\\', '/')
|
||||
path_to_inventory = os.path.abspath(path_to_inventory)
|
||||
assert (os.path.isdir(path_to_inventory)), '{} is no directory'.format(path_to_inventory)
|
||||
if path_to_inventory not in self.inventories:
|
||||
self.inventories.append(path_to_inventory)
|
||||
@@ -220,9 +214,9 @@ class Metadata(object):
|
||||
network_name = network.code
|
||||
if not station_name in self.stations_dict.keys():
|
||||
st_id = '{}.{}'.format(network_name, station_name)
|
||||
self.stations_dict[st_id] = {'latitude': station.latitude,
|
||||
'longitude': station.longitude,
|
||||
'elevation': station.elevation}
|
||||
self.stations_dict[st_id] = {'latitude': station[0].latitude,
|
||||
'longitude': station[0].longitude,
|
||||
'elevation': station[0].elevation}
|
||||
|
||||
read_stat = {'xml': stat_info_from_inventory,
|
||||
'dless': stat_info_from_parser}
|
||||
@@ -268,6 +262,9 @@ class Metadata(object):
|
||||
if not fnames:
|
||||
# search for station name in filename
|
||||
fnames = glob.glob(os.path.join(path_to_inventory, '*' + station + '*'))
|
||||
if not fnames:
|
||||
# search for network name in filename
|
||||
fnames = glob.glob(os.path.join(path_to_inventory, '*' + network + '*'))
|
||||
if not fnames:
|
||||
if self.verbosity:
|
||||
print('Could not find filenames matching station name, network name or seed id')
|
||||
@@ -279,7 +276,7 @@ class Metadata(object):
|
||||
continue
|
||||
invtype, robj = self._read_metadata_file(os.path.join(path_to_inventory, fname))
|
||||
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,
|
||||
'data': robj}
|
||||
if station_seed_id in self.seed_ids.keys():
|
||||
@@ -287,7 +284,6 @@ class Metadata(object):
|
||||
self.seed_ids[station_seed_id] = fname
|
||||
return True
|
||||
except Exception as e:
|
||||
logging.warning(e)
|
||||
continue
|
||||
print('Could not find metadata for station_seed_id {} in path {}'.format(station_seed_id, path_to_inventory))
|
||||
|
||||
@@ -342,19 +338,6 @@ class Metadata(object):
|
||||
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):
|
||||
"""
|
||||
Function takes in date and time as list and validates it's values by trying to make an UTCDateTime object from it
|
||||
@@ -392,167 +375,6 @@ def check_time(datetime):
|
||||
return False
|
||||
|
||||
|
||||
# TODO: change root to datapath
|
||||
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(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 no_metadata(tr, seed_id):
|
||||
print('no metadata file found '
|
||||
@@ -652,8 +474,6 @@ def restitute_data(data, metadata, unit='VEL', force=False, ncores=0):
|
||||
"""
|
||||
|
||||
# data = remove_underscores(data)
|
||||
if not data:
|
||||
return
|
||||
|
||||
# loop over traces
|
||||
input_tuples = []
|
||||
@@ -661,14 +481,9 @@ def restitute_data(data, metadata, unit='VEL', force=False, ncores=0):
|
||||
input_tuples.append((tr, metadata, unit, force))
|
||||
data.remove(tr)
|
||||
|
||||
if ncores == 0:
|
||||
result = []
|
||||
for input_tuple in input_tuples:
|
||||
result.append(restitute_trace(input_tuple))
|
||||
else:
|
||||
pool = gen_Pool(ncores)
|
||||
result = pool.imap_unordered(restitute_trace, input_tuples)
|
||||
pool.close()
|
||||
pool = gen_Pool(ncores)
|
||||
result = pool.imap_unordered(restitute_trace, input_tuples)
|
||||
pool.close()
|
||||
|
||||
for tr, remove_trace in result:
|
||||
if not remove_trace:
|
||||
|
||||
@@ -22,11 +22,14 @@ class Event(ObsPyEvent):
|
||||
:param path: path to event directory
|
||||
:type path: str
|
||||
"""
|
||||
# TODO: remove rootpath and database
|
||||
self.pylot_id = path.split('/')[-1]
|
||||
# initialize super class
|
||||
super(Event, self).__init__(resource_id=ResourceIdentifier('smi:local/' + self.pylot_id))
|
||||
self.path = path
|
||||
self.database = path.split('/')[-2]
|
||||
self.datapath = os.path.split(path)[0] # path.split('/')[-3]
|
||||
self.rootpath = '/' + os.path.join(*path.split('/')[:-3])
|
||||
self.pylot_autopicks = {}
|
||||
self.pylot_picks = {}
|
||||
self.notes = ''
|
||||
|
||||
@@ -20,7 +20,7 @@ import matplotlib
|
||||
matplotlib.use('Qt5Agg')
|
||||
sys.path.append(os.path.join('/'.join(sys.argv[0].split('/')[:-1]), '../../..'))
|
||||
|
||||
from PyLoT import Project
|
||||
from pylot.core.io.project import Project
|
||||
from pylot.core.util.dataprocessing import Metadata
|
||||
from pylot.core.util.array_map import Array_map
|
||||
|
||||
|
||||
+8
-10
@@ -1,7 +1,6 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
import os
|
||||
from functools import lru_cache
|
||||
|
||||
try:
|
||||
import pyqtgraph as pg
|
||||
@@ -26,14 +25,14 @@ def pick_linestyle_pg(picktype, key):
|
||||
:return: Qt line style parameters
|
||||
:rtype:
|
||||
"""
|
||||
linestyles_manu = {'mpp': (QtCore.Qt.SolidLine, 2),
|
||||
'epp': (QtCore.Qt.DashLine, 1),
|
||||
'lpp': (QtCore.Qt.DashLine, 1),
|
||||
'spe': (QtCore.Qt.DashLine, 1)}
|
||||
linestyles_auto = {'mpp': (QtCore.Qt.DotLine, 2),
|
||||
'epp': (QtCore.Qt.DashDotLine, 1),
|
||||
'lpp': (QtCore.Qt.DashDotLine, 1),
|
||||
'spe': (QtCore.Qt.DashDotLine, 1)}
|
||||
linestyles_manu = {'mpp': (QtCore.Qt.SolidLine, 2.),
|
||||
'epp': (QtCore.Qt.DashLine, 1.),
|
||||
'lpp': (QtCore.Qt.DashLine, 1.),
|
||||
'spe': (QtCore.Qt.DashLine, 1.)}
|
||||
linestyles_auto = {'mpp': (QtCore.Qt.DotLine, 2.),
|
||||
'epp': (QtCore.Qt.DashDotLine, 1.),
|
||||
'lpp': (QtCore.Qt.DashDotLine, 1.),
|
||||
'spe': (QtCore.Qt.DashDotLine, 1.)}
|
||||
linestyles = {'manual': linestyles_manu,
|
||||
'auto': linestyles_auto}
|
||||
return linestyles[picktype][key]
|
||||
@@ -81,7 +80,6 @@ def which(program, parameter):
|
||||
return None
|
||||
|
||||
|
||||
@lru_cache(maxsize=128)
|
||||
def make_pen(picktype, phase, key, quality):
|
||||
"""
|
||||
Make PyQtGraph.QPen
|
||||
|
||||
@@ -6,7 +6,7 @@ Created on Wed Jan 26 17:47:25 2015
|
||||
@author: sebastianw
|
||||
"""
|
||||
|
||||
from pylot.core.io.data import SeiscompDataStructure, PilotDataStructure, ObspyDMTdataStructure
|
||||
from pylot.core.io.data import SeiscompDataStructure, PilotDataStructure
|
||||
|
||||
DATASTRUCTURE = {'PILOT': PilotDataStructure, 'SeisComP': SeiscompDataStructure,
|
||||
'obspyDMT': ObspyDMTdataStructure, None: PilotDataStructure}
|
||||
'obspyDMT': PilotDataStructure, None: PilotDataStructure}
|
||||
|
||||
+24
-11
@@ -8,7 +8,6 @@ import platform
|
||||
import re
|
||||
import subprocess
|
||||
import warnings
|
||||
from typing import Literal, Tuple, Type
|
||||
from functools import lru_cache
|
||||
|
||||
import numpy as np
|
||||
@@ -51,6 +50,7 @@ def readDefaultFilterInformation():
|
||||
:rtype: dict
|
||||
"""
|
||||
pparam = PylotParameter()
|
||||
pparam.reset_defaults()
|
||||
return readFilterInformation(pparam)
|
||||
|
||||
|
||||
@@ -106,7 +106,7 @@ def gen_Pool(ncores=0):
|
||||
|
||||
print('gen_Pool: Generated multiprocessing Pool with {} cores\n'.format(ncores))
|
||||
|
||||
pool = multiprocessing.Pool(ncores, maxtasksperchild=100)
|
||||
pool = multiprocessing.Pool(ncores)
|
||||
return pool
|
||||
|
||||
|
||||
@@ -357,8 +357,10 @@ def get_bool(value):
|
||||
False
|
||||
>>> get_bool(None)
|
||||
None
|
||||
>>> get_bool('Stream')
|
||||
'Stream'
|
||||
"""
|
||||
if type(value) is bool:
|
||||
if type(value) == bool:
|
||||
return value
|
||||
elif value in ['True', 'true']:
|
||||
return True
|
||||
@@ -899,6 +901,19 @@ def trim_station_components(data, trim_start=True, trim_end=True):
|
||||
return data
|
||||
|
||||
|
||||
def merge_stream(stream):
|
||||
gaps = stream.get_gaps()
|
||||
if gaps:
|
||||
# list of merged stations (seed_ids)
|
||||
merged = ['{}.{}.{}.{}'.format(*gap[:4]) for gap in gaps]
|
||||
stream.merge(method=1)
|
||||
print('Merged the following stations because of gaps:')
|
||||
for merged_station in merged:
|
||||
print(merged_station)
|
||||
|
||||
return stream, gaps
|
||||
|
||||
|
||||
def check4gapsAndRemove(data):
|
||||
"""
|
||||
check for gaps in Stream and remove them
|
||||
@@ -919,12 +934,12 @@ def check4gapsAndRemove(data):
|
||||
return data
|
||||
|
||||
|
||||
def check_for_gaps_and_merge(data):
|
||||
def check4gapsAndMerge(data):
|
||||
"""
|
||||
check for gaps in Stream and merge if gaps are found
|
||||
:param data: stream of seismic data
|
||||
:type data: `~obspy.core.stream.Stream`
|
||||
:return: data stream, gaps returned from obspy get_gaps
|
||||
:return: data stream
|
||||
:rtype: `~obspy.core.stream.Stream`
|
||||
"""
|
||||
gaps = data.get_gaps()
|
||||
@@ -935,7 +950,7 @@ def check_for_gaps_and_merge(data):
|
||||
for merged_station in merged:
|
||||
print(merged_station)
|
||||
|
||||
return data, gaps
|
||||
return data
|
||||
|
||||
|
||||
def check4doubled(data):
|
||||
@@ -1075,7 +1090,7 @@ def check4rotated(data, metadata=None, verbosity=1):
|
||||
return wfs_in
|
||||
|
||||
# check metadata quality
|
||||
t_start = full_range(wfs_in)[0]
|
||||
t_start = full_range(wfs_in)
|
||||
try:
|
||||
azimuths = []
|
||||
dips = []
|
||||
@@ -1083,9 +1098,8 @@ def check4rotated(data, metadata=None, verbosity=1):
|
||||
azimuths.append(metadata.get_coordinates(tr_id, t_start)['azimuth'])
|
||||
dips.append(metadata.get_coordinates(tr_id, t_start)['dip'])
|
||||
except (KeyError, TypeError) as err:
|
||||
logging.warning(f"Rotating not possible, not all azimuth and dip information "
|
||||
f"available in metadata. Stream remains unchanged.")
|
||||
logging.debug(f"Rotating not possible, {err=}, {type(err)=}")
|
||||
logging.error(f"{type(err)=} occurred: {err=} Rotating not possible, not all azimuth and dip information "
|
||||
f"available in metadata. Stream remains unchanged.")
|
||||
return wfs_in
|
||||
except Exception as err:
|
||||
print(f"Unexpected {err=}, {type(err)=}")
|
||||
@@ -1204,7 +1218,6 @@ def identifyPhase(phase):
|
||||
return False
|
||||
|
||||
|
||||
@lru_cache
|
||||
def identifyPhaseID(phase):
|
||||
"""
|
||||
Returns phase id (capital P or S)
|
||||
|
||||
+143
-154
@@ -8,7 +8,6 @@ import copy
|
||||
import datetime
|
||||
import getpass
|
||||
import glob
|
||||
import logging
|
||||
import multiprocessing
|
||||
import os
|
||||
import subprocess
|
||||
@@ -141,18 +140,6 @@ class LogWidget(QtWidgets.QWidget):
|
||||
self.stderr.append(60 * '#' + '\n\n')
|
||||
|
||||
|
||||
def getDataType(parent):
|
||||
type = QInputDialog().getItem(parent, "Select phases type", "Type:",
|
||||
["manual", "automatic"])
|
||||
|
||||
if type[0].startswith('auto'):
|
||||
type = 'auto'
|
||||
else:
|
||||
type = type[0]
|
||||
|
||||
return type
|
||||
|
||||
|
||||
def plot_pdf(_axes, x, y, annotation, bbox_props, xlabel=None, ylabel=None,
|
||||
title=None):
|
||||
# try method or data
|
||||
@@ -1010,15 +997,6 @@ class WaveformWidgetPG(QtWidgets.QWidget):
|
||||
time_ax = np.linspace(time_ax[0], time_ax[-1], num=len(data))
|
||||
return data, time_ax
|
||||
|
||||
# def getAxes(self):
|
||||
# return self.axes
|
||||
|
||||
# def getXLims(self):
|
||||
# return self.getAxes().get_xlim()
|
||||
|
||||
# def getYLims(self):
|
||||
# return self.getAxes().get_ylim()
|
||||
|
||||
def setXLims(self, lims):
|
||||
vb = self.plotWidget.getPlotItem().getViewBox()
|
||||
vb.setXRange(float(lims[0]), float(lims[1]), padding=0)
|
||||
@@ -1172,8 +1150,6 @@ class PylotCanvas(FigureCanvas):
|
||||
break
|
||||
if not ax_check: return
|
||||
|
||||
# self.updateCurrentLimits() #maybe put this down to else:
|
||||
|
||||
# calculate delta (relative values in axis)
|
||||
old_x, old_y = self.press_rel
|
||||
xdiff = gui_event.x - old_x
|
||||
@@ -1381,125 +1357,140 @@ class PylotCanvas(FigureCanvas):
|
||||
component='*', nth_sample=1, iniPick=None, verbosity=0,
|
||||
plot_additional=False, additional_channel=None, scaleToChannel=None,
|
||||
snr=None):
|
||||
def get_wf_dict(data: Stream = Stream(), linecolor = 'k', offset: float = 0., **plot_kwargs):
|
||||
return dict(data=data, linecolor=linecolor, offset=offset, plot_kwargs=plot_kwargs)
|
||||
ax = self.prepare_plot()
|
||||
self.clearPlotDict()
|
||||
|
||||
wfstart, wfend = self.get_wf_range(wfdata)
|
||||
compclass = self.get_comp_class()
|
||||
plot_streams = self.get_plot_streams(wfdata, wfdata_compare, component, compclass)
|
||||
|
||||
st_main = plot_streams['wfdata']['data']
|
||||
if mapping:
|
||||
plot_positions = self.calcPlotPositions(st_main, compclass)
|
||||
|
||||
nslc = self.get_sorted_nslc(st_main)
|
||||
nmax = self.plot_traces(ax, plot_streams, nslc, wfstart, mapping, plot_positions,
|
||||
scaleToChannel, noiselevel, scaleddata, nth_sample, verbosity)
|
||||
|
||||
if plot_additional and additional_channel:
|
||||
self.plot_additional_trace(ax, wfdata, additional_channel, scaleToChannel,
|
||||
scaleddata, nth_sample, wfstart)
|
||||
|
||||
self.finalize_plot(ax, wfstart, wfend, nmax, zoomx, zoomy, iniPick, title, snr)
|
||||
|
||||
def prepare_plot(self):
|
||||
ax = self.axes[0]
|
||||
ax.cla()
|
||||
return ax
|
||||
|
||||
self.clearPlotDict()
|
||||
wfstart, wfend = full_range(wfdata)
|
||||
nmax = 0
|
||||
def get_wf_range(self, wfdata):
|
||||
return full_range(wfdata)
|
||||
|
||||
def get_comp_class(self):
|
||||
settings = QSettings()
|
||||
compclass = SetChannelComponents.from_qsettings(settings)
|
||||
return SetChannelComponents.from_qsettings(settings)
|
||||
|
||||
def get_plot_streams(self, wfdata, wfdata_compare, component, compclass):
|
||||
def get_wf_dict(data=Stream(), linecolor='k', offset=0., **plot_kwargs):
|
||||
return dict(data=data, linecolor=linecolor, offset=offset, plot_kwargs=plot_kwargs)
|
||||
|
||||
linecolor = (0., 0., 0., 1.) if not self.style else self.style['linecolor']['rgba_mpl']
|
||||
plot_streams = {
|
||||
'wfdata': get_wf_dict(linecolor=linecolor, linewidth=0.7),
|
||||
'wfdata_comp': get_wf_dict(offset=0.1, linecolor='b', alpha=0.7, linewidth=0.5)
|
||||
}
|
||||
|
||||
plot_streams = dict(wfdata=get_wf_dict(linecolor=linecolor, linewidth=0.7),
|
||||
wfdata_comp=get_wf_dict(offset=0.1, linecolor='b', alpha=0.7, linewidth=0.5))
|
||||
|
||||
if not component == '*':
|
||||
if component != '*':
|
||||
alter_comp = compclass.getCompPosition(component)
|
||||
# alter_comp = str(alter_comp[0])
|
||||
|
||||
plot_streams['wfdata']['data'] = wfdata.select(component=component)
|
||||
plot_streams['wfdata']['data'] += wfdata.select(component=alter_comp)
|
||||
plot_streams['wfdata']['data'] = wfdata.select(component=component) + wfdata.select(component=alter_comp)
|
||||
if wfdata_compare:
|
||||
plot_streams['wfdata_comp']['data'] = wfdata_compare.select(component=component)
|
||||
plot_streams['wfdata_comp']['data'] += wfdata_compare.select(component=alter_comp)
|
||||
plot_streams['wfdata_comp']['data'] = wfdata_compare.select(
|
||||
component=component) + wfdata_compare.select(component=alter_comp)
|
||||
else:
|
||||
plot_streams['wfdata']['data'] = wfdata
|
||||
if wfdata_compare:
|
||||
plot_streams['wfdata_comp']['data'] = wfdata_compare
|
||||
|
||||
st_main = plot_streams['wfdata']['data']
|
||||
return plot_streams
|
||||
|
||||
if mapping:
|
||||
plot_positions = self.calcPlotPositions(st_main, compclass)
|
||||
|
||||
# list containing tuples of network, station, channel and plot position (for sorting)
|
||||
nslc = []
|
||||
for plot_pos, trace in enumerate(st_main):
|
||||
if not trace.stats.channel[-1] in ['Z', 'N', 'E', '1', '2', '3']:
|
||||
print('Warning: Unrecognized channel {}'.format(trace.stats.channel))
|
||||
continue
|
||||
nslc.append(trace.get_id())
|
||||
nslc.sort()
|
||||
nslc.reverse()
|
||||
def get_sorted_nslc(self, st_main):
|
||||
nslc = [trace.get_id() for trace in st_main if trace.stats.channel[-1] in ['Z', 'N', 'E', '1', '2', '3']]
|
||||
nslc.sort(reverse=True)
|
||||
return nslc
|
||||
|
||||
def plot_traces(self, ax, plot_streams, nslc, wfstart, mapping, plot_positions, scaleToChannel, noiselevel,
|
||||
scaleddata, nth_sample, verbosity):
|
||||
nmax = 0
|
||||
for n, seed_id in enumerate(nslc):
|
||||
network, station, location, channel = seed_id.split('.')
|
||||
for wf_name, wf_dict in plot_streams.items():
|
||||
st_select = wf_dict.get('data')
|
||||
if not st_select:
|
||||
continue
|
||||
st = st_select.select(id=seed_id)
|
||||
trace = st[0].copy()
|
||||
trace = st_select.select(id=seed_id)[0].copy()
|
||||
if mapping:
|
||||
n = plot_positions[trace.stats.channel]
|
||||
if n > nmax:
|
||||
nmax = n
|
||||
if verbosity:
|
||||
msg = 'plotting %s channel of station %s' % (channel, station)
|
||||
print(msg)
|
||||
stime = trace.stats.starttime - wfstart
|
||||
time_ax = prep_time_axis(stime, trace)
|
||||
if time_ax is not None:
|
||||
if scaleToChannel:
|
||||
st_scale = wfdata.select(channel=scaleToChannel)
|
||||
if st_scale:
|
||||
tr = st_scale[0]
|
||||
trace.detrend('constant')
|
||||
trace.normalize(np.max(np.abs(tr.data)) * 2)
|
||||
scaleddata = True
|
||||
if not scaleddata:
|
||||
trace.detrend('constant')
|
||||
trace.normalize(np.max(np.abs(trace.data)) * 2)
|
||||
print(f'plotting {channel} channel of station {station}')
|
||||
time_ax = prep_time_axis(trace.stats.starttime - wfstart, trace)
|
||||
self.plot_trace(ax, trace, time_ax, wf_dict, n, scaleToChannel, noiselevel, scaleddata, nth_sample)
|
||||
self.setPlotDict(n, seed_id)
|
||||
return nmax
|
||||
|
||||
offset = wf_dict.get('offset')
|
||||
def plot_trace(self, ax, trace, time_ax, wf_dict, n, scaleToChannel, noiselevel, scaleddata, nth_sample):
|
||||
if time_ax is not None:
|
||||
if scaleToChannel:
|
||||
self.scale_trace(trace, scaleToChannel)
|
||||
scaleddata = True
|
||||
if not scaleddata:
|
||||
trace.detrend('constant')
|
||||
trace.normalize(np.max(np.abs(trace.data)) * 2)
|
||||
offset = wf_dict.get('offset')
|
||||
|
||||
times = [time for index, time in enumerate(time_ax) if not index % nth_sample]
|
||||
data = [datum + n + offset for index, datum in enumerate(trace.data) if not index % nth_sample]
|
||||
ax.axhline(n, color="0.5", lw=0.5)
|
||||
ax.plot(times, data, color=wf_dict.get('linecolor'), **wf_dict.get('plot_kwargs'))
|
||||
if noiselevel is not None:
|
||||
for level in [-noiselevel[channel], noiselevel[channel]]:
|
||||
ax.plot([time_ax[0], time_ax[-1]],
|
||||
[n + level, n + level],
|
||||
color=wf_dict.get('linecolor'),
|
||||
linestyle='dashed')
|
||||
self.setPlotDict(n, seed_id)
|
||||
if plot_additional and additional_channel:
|
||||
compare_stream = wfdata.select(channel=additional_channel)
|
||||
if compare_stream:
|
||||
trace = compare_stream[0]
|
||||
if scaleToChannel:
|
||||
st_scale = wfdata.select(channel=scaleToChannel)
|
||||
if st_scale:
|
||||
tr = st_scale[0]
|
||||
trace.detrend('constant')
|
||||
trace.normalize(np.max(np.abs(tr.data)) * 2)
|
||||
scaleddata = True
|
||||
if not scaleddata:
|
||||
trace.detrend('constant')
|
||||
trace.normalize(np.max(np.abs(trace.data)) * 2)
|
||||
time_ax = prep_time_axis(stime, trace)
|
||||
times = [time for index, time in enumerate(time_ax) if not index % nth_sample]
|
||||
p_data = compare_stream[0].data
|
||||
# #normalize
|
||||
# p_max = max(abs(p_data))
|
||||
# p_data /= p_max
|
||||
for index in range(3):
|
||||
ax.plot(times, p_data, color='red', alpha=0.5, linewidth=0.7)
|
||||
p_data += 1
|
||||
times = [time for index, time in enumerate(time_ax) if not index % nth_sample]
|
||||
data = [datum + n + offset for index, datum in enumerate(trace.data) if not index % nth_sample]
|
||||
ax.axhline(n, color="0.5", lw=0.5)
|
||||
ax.plot(times, data, color=wf_dict.get('linecolor'), **wf_dict.get('plot_kwargs'))
|
||||
if noiselevel is not None:
|
||||
self.plot_noise_level(ax, time_ax, noiselevel, channel, n, wf_dict.get('linecolor'))
|
||||
|
||||
def scale_trace(self, trace, scaleToChannel):
|
||||
st_scale = wfdata.select(channel=scaleToChannel)
|
||||
if st_scale:
|
||||
tr = st_scale[0]
|
||||
trace.detrend('constant')
|
||||
trace.normalize(np.max(np.abs(tr.data)) * 2)
|
||||
|
||||
def plot_noise_level(self, ax, time_ax, noiselevel, channel, n, linecolor):
|
||||
for level in [-noiselevel[channel], noiselevel[channel]]:
|
||||
ax.plot([time_ax[0], time_ax[-1]], [n + level, n + level], color=linecolor, linestyle='dashed')
|
||||
|
||||
def plot_additional_trace(self, ax, wfdata, additional_channel, scaleToChannel, scaleddata, nth_sample, wfstart):
|
||||
compare_stream = wfdata.select(channel=additional_channel)
|
||||
if compare_stream:
|
||||
trace = compare_stream[0]
|
||||
if scaleToChannel:
|
||||
self.scale_trace(trace, scaleToChannel)
|
||||
scaleddata = True
|
||||
if not scaleddata:
|
||||
trace.detrend('constant')
|
||||
trace.normalize(np.max(np.abs(trace.data)) * 2)
|
||||
time_ax = prep_time_axis(trace.stats.starttime - wfstart, trace)
|
||||
self.plot_additional_data(ax, trace, time_ax, nth_sample)
|
||||
|
||||
def plot_additional_data(self, ax, trace, time_ax, nth_sample):
|
||||
times = [time for index, time in enumerate(time_ax) if not index % nth_sample]
|
||||
p_data = trace.data
|
||||
for index in range(3):
|
||||
ax.plot(times, p_data, color='red', alpha=0.5, linewidth=0.7)
|
||||
p_data += 1
|
||||
|
||||
def finalize_plot(self, ax, wfstart, wfend, nmax, zoomx, zoomy, iniPick, title, snr):
|
||||
if iniPick:
|
||||
ax.vlines(iniPick, ax.get_ylim()[0], ax.get_ylim()[1],
|
||||
colors='m', linestyles='dashed',
|
||||
linewidth=2)
|
||||
xlabel = 'seconds since {0}'.format(wfstart)
|
||||
ax.vlines(iniPick, ax.get_ylim()[0], ax.get_ylim()[1], colors='m', linestyles='dashed', linewidth=2)
|
||||
xlabel = f'seconds since {wfstart}'
|
||||
ylabel = ''
|
||||
self.updateWidget(xlabel, ylabel, title)
|
||||
self.setXLims(ax, [0, wfend - wfstart])
|
||||
@@ -1509,15 +1500,14 @@ class PylotCanvas(FigureCanvas):
|
||||
if zoomy is not None:
|
||||
self.setYLims(ax, zoomy)
|
||||
if snr is not None:
|
||||
if snr < 2:
|
||||
warning = 'LOW SNR'
|
||||
if snr < 1.5:
|
||||
warning = 'VERY LOW SNR'
|
||||
ax.text(0.1, 0.9, 'WARNING - {}'.format(warning), ha='center', va='center', transform=ax.transAxes,
|
||||
color='red')
|
||||
|
||||
self.plot_snr_warning(ax, snr)
|
||||
self.draw()
|
||||
|
||||
def plot_snr_warning(self, ax, snr):
|
||||
if snr < 2:
|
||||
warning = 'LOW SNR' if snr >= 1.5 else 'VERY LOW SNR'
|
||||
ax.text(0.1, 0.9, f'WARNING - {warning}', ha='center', va='center', transform=ax.transAxes, color='red')
|
||||
|
||||
@staticmethod
|
||||
def getXLims(ax):
|
||||
return ax.get_xlim()
|
||||
@@ -1871,14 +1861,13 @@ class PickDlg(QDialog):
|
||||
|
||||
def __init__(self, parent=None, data=None, data_compare=None, station=None, network=None, location=None, picks=None,
|
||||
autopicks=None, rotate=False, parameter=None, embedded=False, metadata=None, show_comp_data=False,
|
||||
event=None, filteroptions=None, wftype=None):
|
||||
event=None, filteroptions=None, model=None, wftype=None):
|
||||
super(PickDlg, self).__init__(parent, Qt.Window)
|
||||
self.orig_parent = parent
|
||||
self.setAttribute(Qt.WA_DeleteOnClose)
|
||||
|
||||
# initialize attributes
|
||||
self.parameter = parameter
|
||||
model = self.parameter.get('taup_model')
|
||||
self._embedded = embedded
|
||||
self.showCompData = show_comp_data
|
||||
self.station = station
|
||||
@@ -1923,7 +1912,7 @@ class PickDlg(QDialog):
|
||||
# set attribute holding data
|
||||
if data is None or not data:
|
||||
try:
|
||||
data = parent.get_data().getWFData().copy()
|
||||
data = parent.get_data().get_wf_data().copy()
|
||||
self.data = data.select(station=station)
|
||||
except AttributeError as e:
|
||||
errmsg = 'You either have to put in a data or an appropriate ' \
|
||||
@@ -1957,7 +1946,7 @@ class PickDlg(QDialog):
|
||||
self.cur_xlim = None
|
||||
self.cur_ylim = None
|
||||
|
||||
self.stime, self.etime = full_range(self.getWFData())
|
||||
self.stime, self.etime = full_range(self.get_wf_data())
|
||||
|
||||
# initialize plotting widget
|
||||
self.multicompfig = PylotCanvas(parent=self, multicursor=True)
|
||||
@@ -1971,7 +1960,7 @@ class PickDlg(QDialog):
|
||||
self.referenceChannel.addItem('-', None)
|
||||
self.scaleChannel.addItem('individual', None)
|
||||
|
||||
for trace in self.getWFData():
|
||||
for trace in self.get_wf_data():
|
||||
channel = trace.stats.channel
|
||||
self.referenceChannel.addItem(channel, trace)
|
||||
if not channel[-1] in ['Z', 'N', 'E', '1', '2', '3']:
|
||||
@@ -1990,7 +1979,7 @@ class PickDlg(QDialog):
|
||||
if self.wftype is not None:
|
||||
title += ' | ({})'.format(self.wftype)
|
||||
|
||||
self.multicompfig.plotWFData(wfdata=self.getWFData(), wfdata_compare=self.getWFDataComp(),
|
||||
self.multicompfig.plotWFData(wfdata=self.get_wf_data(), wfdata_compare=self.get_wf_dataComp(),
|
||||
title=title)
|
||||
|
||||
self.multicompfig.setZoomBorders2content()
|
||||
@@ -2271,8 +2260,8 @@ class PickDlg(QDialog):
|
||||
arrivals = func[plot](source_origin.depth,
|
||||
source_origin.latitude,
|
||||
source_origin.longitude,
|
||||
station_coords.get('latitude'),
|
||||
station_coords.get('longitude'),
|
||||
station_coords['latitude'],
|
||||
station_coords['longitude'],
|
||||
phases)
|
||||
self.arrivals = arrivals
|
||||
|
||||
@@ -2525,7 +2514,7 @@ class PickDlg(QDialog):
|
||||
if self.autoFilterAction.isChecked():
|
||||
for filteraction in [self.filterActionP, self.filterActionS]:
|
||||
filteraction.setChecked(False)
|
||||
self.filterWFData()
|
||||
self.filter_wf_data()
|
||||
self.draw()
|
||||
else:
|
||||
self.draw()
|
||||
@@ -2609,10 +2598,10 @@ class PickDlg(QDialog):
|
||||
def getGlobalLimits(self, ax, axis):
|
||||
return self.multicompfig.getGlobalLimits(ax, axis)
|
||||
|
||||
def getWFData(self):
|
||||
def get_wf_data(self):
|
||||
return self.data
|
||||
|
||||
def getWFDataComp(self):
|
||||
def get_wf_dataComp(self):
|
||||
if self.showCompData:
|
||||
return self.data_compare
|
||||
else:
|
||||
@@ -2627,17 +2616,17 @@ class PickDlg(QDialog):
|
||||
return tr
|
||||
|
||||
if component == 'E' or component == 'N':
|
||||
for trace in self.getWFData():
|
||||
for trace in self.get_wf_data():
|
||||
trace = selectTrace(trace, 'NE')
|
||||
if trace:
|
||||
wfdata.append(trace)
|
||||
elif component == '1' or component == '2':
|
||||
for trace in self.getWFData():
|
||||
for trace in self.get_wf_data():
|
||||
trace = selectTrace(trace, '12')
|
||||
if trace:
|
||||
wfdata.append(trace)
|
||||
else:
|
||||
wfdata = self.getWFData().select(component=component)
|
||||
wfdata = self.get_wf_data().select(component=component)
|
||||
return wfdata
|
||||
|
||||
def getPicks(self, picktype='manual'):
|
||||
@@ -2740,8 +2729,8 @@ class PickDlg(QDialog):
|
||||
stime = self.getStartTime()
|
||||
|
||||
# copy wfdata for plotting
|
||||
wfdata = self.getWFData().copy()
|
||||
wfdata_comp = self.getWFDataComp().copy()
|
||||
wfdata = self.get_wf_data().copy()
|
||||
wfdata_comp = self.get_wf_dataComp().copy()
|
||||
wfdata = self.getPickPhases(wfdata, phase)
|
||||
wfdata_comp = self.getPickPhases(wfdata_comp, phase)
|
||||
for wfd in [wfdata, wfdata_comp]:
|
||||
@@ -2850,7 +2839,7 @@ class PickDlg(QDialog):
|
||||
filteroptions = None
|
||||
|
||||
# copy and filter data for earliest and latest possible picks
|
||||
wfdata = self.getWFData().copy().select(channel=channel)
|
||||
wfdata = self.get_wf_data().copy().select(channel=channel)
|
||||
if filteroptions:
|
||||
try:
|
||||
wfdata.detrend('linear')
|
||||
@@ -2897,7 +2886,7 @@ class PickDlg(QDialog):
|
||||
minFMSNR = parameter.get('minFMSNR')
|
||||
quality = get_quality_class(spe, parameter.get('timeerrorsP'))
|
||||
if quality <= minFMweight and snr >= minFMSNR:
|
||||
FM = fmpicker(self.getWFData().select(channel=channel).copy(), wfdata.copy(), parameter.get('fmpickwin'),
|
||||
FM = fmpicker(self.get_wf_data().select(channel=channel).copy(), wfdata.copy(), parameter.get('fmpickwin'),
|
||||
pick - stime_diff)
|
||||
|
||||
# save pick times for actual phase
|
||||
@@ -3189,7 +3178,7 @@ class PickDlg(QDialog):
|
||||
def togglePickBlocker(self):
|
||||
return not self.pick_block
|
||||
|
||||
def filterWFData(self, phase=None):
|
||||
def filter_wf_data(self, phase=None):
|
||||
if not phase:
|
||||
phase = self.currentPhase
|
||||
if self.getPhaseID(phase) == 'P':
|
||||
@@ -3207,8 +3196,8 @@ class PickDlg(QDialog):
|
||||
self.cur_xlim = self.multicompfig.axes[0].get_xlim()
|
||||
self.cur_ylim = self.multicompfig.axes[0].get_ylim()
|
||||
# self.multicompfig.updateCurrentLimits()
|
||||
wfdata = self.getWFData().copy()
|
||||
wfdata_comp = self.getWFDataComp().copy()
|
||||
wfdata = self.get_wf_data().copy()
|
||||
wfdata_comp = self.get_wf_dataComp().copy()
|
||||
title = self.getStation()
|
||||
if filter:
|
||||
filtoptions = None
|
||||
@@ -3245,14 +3234,14 @@ class PickDlg(QDialog):
|
||||
def filterP(self):
|
||||
self.filterActionS.setChecked(False)
|
||||
if self.filterActionP.isChecked():
|
||||
self.filterWFData('P')
|
||||
self.filter_wf_data('P')
|
||||
else:
|
||||
self.refreshPlot()
|
||||
|
||||
def filterS(self):
|
||||
self.filterActionP.setChecked(False)
|
||||
if self.filterActionS.isChecked():
|
||||
self.filterWFData('S')
|
||||
self.filter_wf_data('S')
|
||||
else:
|
||||
self.refreshPlot()
|
||||
|
||||
@@ -3311,7 +3300,7 @@ class PickDlg(QDialog):
|
||||
if self.autoFilterAction.isChecked():
|
||||
self.filterActionP.setChecked(False)
|
||||
self.filterActionS.setChecked(False)
|
||||
# data = self.getWFData().copy()
|
||||
# data = self.get_wf_data().copy()
|
||||
# title = self.getStation()
|
||||
filter = False
|
||||
phase = None
|
||||
@@ -3811,8 +3800,8 @@ class TuneAutopicker(QWidget):
|
||||
fnames = self.station_ids[self.get_current_station_id()]
|
||||
if not fnames == self.fnames:
|
||||
self.fnames = fnames
|
||||
self.data.setWFData(fnames)
|
||||
wfdat = self.data.getWFData() # all available streams
|
||||
self.data.set_wf_data(fnames)
|
||||
wfdat = self.data.get_wf_data() # all available streams
|
||||
# remove possible underscores in station names
|
||||
# wfdat = remove_underscores(wfdat)
|
||||
# rotate misaligned stations to ZNE
|
||||
@@ -3837,7 +3826,7 @@ class TuneAutopicker(QWidget):
|
||||
self.stb_names = ['aicARHfig', 'refSpick', 'el_S1pick', 'el_S2pick']
|
||||
|
||||
def add_parameters(self):
|
||||
self.paraBox = PylotParameterWidget(self.parameter, parent=self, windowflag=Qt.Widget)
|
||||
self.paraBox = PylotParaBox(self.parameter, parent=self, windowflag=Qt.Widget)
|
||||
self.paraBox.set_tune_mode(True)
|
||||
self.update_eventID()
|
||||
self.parameter_layout.addWidget(self.paraBox)
|
||||
@@ -3917,8 +3906,8 @@ class TuneAutopicker(QWidget):
|
||||
network = None
|
||||
location = None
|
||||
|
||||
wfdata = self.data.getWFData()
|
||||
wfdata_comp = self.data.getWFDataComp()
|
||||
wfdata = self.data.get_wf_data()
|
||||
wfdata_comp = self.data.get_wf_dataComp()
|
||||
metadata = self.parent().metadata
|
||||
event = self.get_current_event()
|
||||
filteroptions = self.parent().filteroptions
|
||||
@@ -3973,7 +3962,7 @@ class TuneAutopicker(QWidget):
|
||||
for plotitem in self._manual_pick_plots:
|
||||
self.clear_plotitem(plotitem)
|
||||
self._manual_pick_plots = []
|
||||
st = self.data.getWFData()
|
||||
st = self.data.get_wf_data()
|
||||
tr = st.select(station=self.get_current_station())[0]
|
||||
starttime = tr.stats.starttime
|
||||
# create two lists with figure names and subindices (for subplots) to get the correct axes
|
||||
@@ -4204,7 +4193,7 @@ class TuneAutopicker(QWidget):
|
||||
self.qmb.show()
|
||||
|
||||
|
||||
class PylotParameterWidget(QtWidgets.QWidget):
|
||||
class PylotParaBox(QtWidgets.QWidget):
|
||||
accepted = QtCore.Signal(str)
|
||||
rejected = QtCore.Signal(str)
|
||||
|
||||
@@ -4318,11 +4307,6 @@ class PylotParameterWidget(QtWidgets.QWidget):
|
||||
grid = QtWidgets.QGridLayout()
|
||||
|
||||
for index1, name in enumerate(parameter_names):
|
||||
if name in ['rootpath', 'database']:
|
||||
logging.warning(
|
||||
f'Deprecated parameter loaded: {name}. Check if datapath is still correct in parameter widget.'
|
||||
)
|
||||
continue
|
||||
default_item = self.parameter.get_defaults()[name]
|
||||
tooltip = default_item['tooltip']
|
||||
tooltip += ' | type: {}'.format(default_item['type'])
|
||||
@@ -4892,7 +4876,7 @@ class PropTab(QWidget):
|
||||
def getValues(self):
|
||||
return None
|
||||
|
||||
def resetValues(self, infile):
|
||||
def resetValues(self, infile=None):
|
||||
return None
|
||||
|
||||
|
||||
@@ -4989,7 +4973,12 @@ class InputsTab(PropTab):
|
||||
else:
|
||||
index = 2
|
||||
datapath = para.get('datapath') if not para.get('datapath') is None else ''
|
||||
values = {"data/dataRoot": self.dataDirEdit.setText("%s" % datapath),
|
||||
rootpath = para.get('rootpath') if not para.get('rootpath') is None else ''
|
||||
database = para.get('database') if not para.get('database') is None else ''
|
||||
if isinstance(database, int):
|
||||
database = str(database)
|
||||
path = os.path.join(os.path.expanduser('~'), rootpath, datapath, database)
|
||||
values = {"data/dataRoot": self.dataDirEdit.setText("%s" % path),
|
||||
"user/FullName": self.fullNameEdit.text(),
|
||||
"data/Structure": self.structureSelect.setCurrentIndex(index),
|
||||
"tstart": self.tstartBox.setValue(0),
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
@@ -1,101 +0,0 @@
|
||||
############################# correlation parameters #####################################
|
||||
# min_corr_stacking: minimum correlation coefficient for building beam trace
|
||||
# min_corr_export: minimum correlation coefficient for pick export
|
||||
# min_stack: minimum number of stations for building beam trace
|
||||
# t_before: correlation window before pick
|
||||
# t_after: correlation window after pick#
|
||||
# cc_maxlag: maximum shift for initial correlation
|
||||
# cc_maxlag2: maximum shift for second (final) correlation (also for calculating pick uncertainty)
|
||||
# initial_pick_outlier_threshold: (hopefully) threshold for excluding large outliers of initial (AIC) picks
|
||||
# export_threshold: automatically exclude all onsets which deviate more than this threshold from corrected taup onsets
|
||||
# min_picks_export: minimum number of correlated picks for export
|
||||
# min_picks_autopylot: minimum number of reference auto picks to continue with event
|
||||
# check_RMS: do RMS check to search for restitution errors (very experimental)
|
||||
# use_taupy_onsets: use taupy onsets as reference picks instead of external picks
|
||||
# station_list: use the following stations as reference for stacking
|
||||
# use_stacked_trace: use existing stacked trace if found (spare re-computation)
|
||||
# data_dir: obspyDMT data subdirectory (e.g. 'raw', 'processed')
|
||||
# pickfile_extension: use quakeML files (PyLoT output) with the following extension, e.g. '_autopylot' for pickfiles
|
||||
# such as 'PyLoT_20170501_141822_autopylot.xml'
|
||||
# dt_stacking: time shift for stacking (e.g. [0, 250] for 0 and 250 seconds shift)
|
||||
# filter_options: filter for first correlation (rough)
|
||||
# filter_options_final: filter for second correlation (fine)
|
||||
# filter_type: e.g. 'bandpass'
|
||||
# sampfreq: sampling frequency of the data
|
||||
|
||||
logging: info
|
||||
pick_phases: ['P', 'S']
|
||||
|
||||
# P-phase
|
||||
P:
|
||||
min_corr_stacking: 0.8
|
||||
min_corr_export: 0.6
|
||||
min_stack: 20
|
||||
t_before: 30.
|
||||
t_after: 50.
|
||||
cc_maxlag: 50.
|
||||
cc_maxlag2: 5.
|
||||
initial_pick_outlier_threshold: 30.
|
||||
export_threshold: 2.5
|
||||
min_picks_export: 100
|
||||
min_picks_autopylot: 50
|
||||
check_RMS: True
|
||||
use_taupy_onsets: False
|
||||
station_list: ['HU.MORH', 'HU.TIH', 'OX.FUSE', 'OX.BAD']
|
||||
use_stacked_trace: False
|
||||
data_dir: 'processed'
|
||||
pickfile_extension: '_autopylot'
|
||||
dt_stacking: [250, 250]
|
||||
|
||||
# filter for first correlation (rough)
|
||||
filter_options:
|
||||
freqmax: 0.5
|
||||
freqmin: 0.03
|
||||
# filter for second correlation (fine)
|
||||
filter_options_final:
|
||||
freqmax: 0.5
|
||||
freqmin: 0.03
|
||||
|
||||
filter_type: bandpass
|
||||
sampfreq: 20.0
|
||||
|
||||
# ignore if autopylot fails to pick master-trace (not recommended if absolute onset times matter)
|
||||
ignore_autopylot_fail_on_master: True
|
||||
|
||||
# S-phase
|
||||
S:
|
||||
min_corr_stacking: 0.7
|
||||
min_corr_export: 0.6
|
||||
min_stack: 20
|
||||
t_before: 60.
|
||||
t_after: 60.
|
||||
cc_maxlag: 100.
|
||||
cc_maxlag2: 25.
|
||||
initial_pick_outlier_threshold: 30.
|
||||
export_threshold: 5.0
|
||||
min_picks_export: 200
|
||||
min_picks_autopylot: 50
|
||||
check_RMS: True
|
||||
use_taupy_onsets: False
|
||||
station_list: ['HU.MORH','HU.TIH', 'OX.FUSE', 'OX.BAD']
|
||||
use_stacked_trace: False
|
||||
data_dir: 'processed'
|
||||
pickfile_extension: '_autopylot'
|
||||
dt_stacking: [250, 250]
|
||||
|
||||
# filter for first correlation (rough)
|
||||
filter_options:
|
||||
freqmax: 0.1
|
||||
freqmin: 0.01
|
||||
|
||||
# filter for second correlation (fine)
|
||||
filter_options_final:
|
||||
freqmax: 0.2
|
||||
freqmin: 0.01
|
||||
|
||||
filter_type: bandpass
|
||||
sampfreq: 20.0
|
||||
|
||||
# ignore if autopylot fails to pick master-trace (not recommended if absolute onset times matter)
|
||||
ignore_autopylot_fail_on_master: True
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,41 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
#ulimit -s 8192
|
||||
#ulimit -v $(ulimit -v | awk '{printf("%d",$1*0.95)}')
|
||||
#ulimit -v
|
||||
|
||||
#655360
|
||||
|
||||
source /opt/anaconda3/etc/profile.d/conda.sh
|
||||
conda activate pylot_311
|
||||
NSLOTS=20
|
||||
|
||||
#qsub -l low -cwd -l "os=*stretch" -pe smp 40 submit_pick_corr_correction.sh
|
||||
#$ -l low
|
||||
#$ -l h_vmem=6G
|
||||
#$ -cwd
|
||||
#$ -pe smp 20
|
||||
#$ -N corr_pick
|
||||
|
||||
|
||||
export PYTHONPATH="$PYTHONPATH:/home/marcel/git/pylot_tools/"
|
||||
export PYTHONPATH="$PYTHONPATH:/home/marcel/git/"
|
||||
export PYTHONPATH="$PYTHONPATH:/home/marcel/git/pylot/"
|
||||
|
||||
#export MKL_NUM_THREADS=${NSLOTS:=1}
|
||||
#export NUMEXPR_NUM_THREADS=${NSLOTS:=1}
|
||||
#export OMP_NUM_THREADS=${NSLOTS:=1}
|
||||
|
||||
#python pick_correlation_correction.py '/data/AlpArray_Data/dmt_database_mantle_M5.8-6.0' '/home/marcel/.pylot/pylot_alparray_mantle_corr_stack_0.03-0.5.in' -pd -n ${NSLOTS:=1} -istart 0 -istop 100
|
||||
#python pick_correlation_correction.py '/data/AlpArray_Data/dmt_database_mantle_M5.8-6.0' '/home/marcel/.pylot/pylot_alparray_mantle_corr_stack_0.03-0.5.in' -pd -n ${NSLOTS:=1} -istart 100 -istop 200
|
||||
#python pick_correlation_correction.py '/data/AlpArray_Data/dmt_database_mantle_M6.0-6.5' '/home/marcel/.pylot/pylot_alparray_mantle_corr_stack_0.03-0.5.in' -pd -n ${NSLOTS:=1} -istart 0 -istop 100
|
||||
#python pick_correlation_correction.py '/data/AlpArray_Data/dmt_database_mantle_M5.8-6.0' '/home/marcel/.pylot/pylot_alparray_mantle_corr_stack_0.03-0.5.in' -pd -n ${NSLOTS:=1} -istart 100 -istop 200
|
||||
#python pick_correlation_correction.py 'H:\sciebo\dmt_database' 'H:\Sciebo\dmt_database\pylot_alparray_mantle_corr_S_0.01-0.2.in' -pd -n 4 -t
|
||||
|
||||
#pylot_infile='/home/marcel/.pylot/pylot_alparray_syn_fwi_mk6_it3.in'
|
||||
pylot_infile='/home/marcel/.pylot/pylot_adriaarray_corr_P_and_S.in'
|
||||
|
||||
# THIS SCRIPT SHOLD BE CALLED BY "submit_to_grid_engine.py" using the following line:
|
||||
# use -pd for detailed plots in eventdir/correlation_XX_XX/figures
|
||||
python pick_correlation_correction.py $1 $pylot_infile -n ${NSLOTS:=1} -istart $2 --params 'parameters_adriaarray.yaml' # -pd
|
||||
#--event_blacklist eventlist.txt
|
||||
@@ -1,28 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import subprocess
|
||||
|
||||
fnames = [
|
||||
('/data/AdriaArray_Data/dmt_database_mantle_M5.0-5.4', 0),
|
||||
('/data/AdriaArray_Data/dmt_database_mantle_M5.4-5.7', 0),
|
||||
('/data/AdriaArray_Data/dmt_database_mantle_M5.7-6.0', 0),
|
||||
('/data/AdriaArray_Data/dmt_database_mantle_M6.0-6.3', 0),
|
||||
('/data/AdriaArray_Data/dmt_database_mantle_M6.3-10.0', 0),
|
||||
# ('/data/AdriaArray_Data/dmt_database_ISC_mantle_M5.0-5.4', 0),
|
||||
# ('/data/AdriaArray_Data/dmt_database_ISC_mantle_M5.4-5.7', 0),
|
||||
# ('/data/AdriaArray_Data/dmt_database_ISC_mantle_M5.7-6.0', 0),
|
||||
# ('/data/AdriaArray_Data/dmt_database_ISC_mantle_M6.0-10.0', 0),
|
||||
]
|
||||
|
||||
#fnames = [('/data/AlpArray_Data/dmt_database_mantle_0.01-0.2_SKS-phase', 0),
|
||||
# ('/data/AlpArray_Data/dmt_database_mantle_0.01-0.2_S-phase', 0),]
|
||||
|
||||
####
|
||||
script_location = '/home/marcel/VersionCtrl/git/pylot/pylot/correlation/submit_pick_corr_correction.sh'
|
||||
####
|
||||
|
||||
for fnin, istart in fnames:
|
||||
input_cmds = f'qsub -q low.q@minos15,low.q@minos14,low.q@minos13,low.q@minos12,low.q@minos11 {script_location} {fnin} {istart}'
|
||||
|
||||
print(input_cmds)
|
||||
print(subprocess.check_output(input_cmds.split()))
|
||||
@@ -1,61 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import os
|
||||
import glob
|
||||
import json
|
||||
|
||||
from obspy import read_events
|
||||
|
||||
from pylot.core.util.dataprocessing import Metadata
|
||||
from pylot.core.util.obspyDMT_interface import qml_from_obspyDMT
|
||||
|
||||
|
||||
def get_event_obspy_dmt(eventdir):
|
||||
event_pkl_file = os.path.join(eventdir, 'info', 'event.pkl')
|
||||
if not os.path.exists(event_pkl_file):
|
||||
raise IOError('Could not find event path for event: {}'.format(eventdir))
|
||||
event = qml_from_obspyDMT(event_pkl_file)
|
||||
return event
|
||||
|
||||
|
||||
def get_event_pylot(eventdir, extension=''):
|
||||
event_id = get_event_id(eventdir)
|
||||
filename = os.path.join(eventdir, 'PyLoT_{}{}.xml'.format(event_id, extension))
|
||||
if not os.path.isfile(filename):
|
||||
return
|
||||
cat = read_events(filename)
|
||||
return cat[0]
|
||||
|
||||
|
||||
def get_event_id(eventdir):
|
||||
event_id = os.path.split(eventdir)[-1]
|
||||
return event_id
|
||||
|
||||
|
||||
def get_picks(eventdir, extension=''):
|
||||
event_id = get_event_id(eventdir)
|
||||
filename = 'PyLoT_{}{}.xml'
|
||||
filename = filename.format(event_id, extension)
|
||||
fpath = os.path.join(eventdir, filename)
|
||||
fpaths = glob.glob(fpath)
|
||||
if len(fpaths) == 1:
|
||||
cat = read_events(fpaths[0])
|
||||
picks = cat[0].picks
|
||||
return picks
|
||||
elif len(fpaths) == 0:
|
||||
print('get_picks: File not found: {}'.format(fpath))
|
||||
return
|
||||
print(f'WARNING: Ambiguous pick file specification. Found the following pick files {fpaths}\nFilemask: {fpath}')
|
||||
return
|
||||
|
||||
|
||||
def write_json(object, fname):
|
||||
with open(fname, 'w') as outfile:
|
||||
json.dump(object, outfile, sort_keys=True, indent=4)
|
||||
|
||||
|
||||
def get_metadata(eventdir):
|
||||
metadata_path = os.path.join(eventdir, 'resp')
|
||||
metadata = Metadata(inventory=metadata_path, verbosity=0)
|
||||
return metadata
|
||||
@@ -1,23 +0,0 @@
|
||||
from pylot.tomography.fmtomo_utils import Tomo3d
|
||||
import os
|
||||
|
||||
citer = 0
|
||||
niter = 12
|
||||
n_proc = 4 # only four processes for minimal example with four sources
|
||||
|
||||
# for some reason this did not work as expected and was commented out
|
||||
#if os.path.isfile('inviter.in'):
|
||||
# with open('inviter.in', 'r') as infile:
|
||||
# citer = int(infile.read())
|
||||
# print ('Continue on iteration step ', citer)
|
||||
|
||||
tomo = Tomo3d(os.getcwd(), os.getcwd(), overwrite=True, buildObs=False, saveRays=[6, 12], citer=citer)
|
||||
|
||||
try:
|
||||
tomo.runTOMO3D(n_proc, niter)
|
||||
except KeyboardInterrupt:
|
||||
print('runTTOMO3D interrupted by user or machine. Cleaning up.')
|
||||
except Exception as e:
|
||||
print(f'Catching unknown Exception in runTOMO3D: {e}. Trying to clean up...')
|
||||
finally:
|
||||
tomo.removeDirectories()
|
||||
@@ -1,2 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
@@ -1,177 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Small script to compare arrival times of hybrid tau-p/fmm with simple tau-p for standard earth model
|
||||
|
||||
import os
|
||||
import argparse
|
||||
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
from obspy.taup import TauPyModel
|
||||
|
||||
from pylot.tomography.fmtomo_tools.fmtomo_teleseismic_utils import organize_receivers, organize_sources, organize_event_names
|
||||
|
||||
|
||||
def read_file(fnin):
|
||||
infile = open(fnin, 'r')
|
||||
return infile.readlines()
|
||||
|
||||
|
||||
def compare_arrivals(arrivals_file, receivers_file, sources_file, input_source_file, model='ak135_diehl_v2', exclude_phases=[]):
|
||||
'''
|
||||
Reads FMTOMO arrivals.dat file together with corresponding receiver, source and input_source files to match
|
||||
each arrival to a source and receiver combination, calculate tau-p arrivals for a given earth model and
|
||||
calculate differences
|
||||
'''
|
||||
arrivals_taup = {}
|
||||
arrivals_tomo = {}
|
||||
receiver_ids = {}
|
||||
event_names = {}
|
||||
events = organize_event_names(input_source_file)
|
||||
model = TauPyModel(model)
|
||||
|
||||
# organize sources and receivers in dictionaries containing their ID used by FMTOMO as dictionary key
|
||||
receivers_dict = organize_receivers(receivers_file)
|
||||
sources_dict = organize_sources(sources_file)
|
||||
|
||||
# read arrivals file
|
||||
with open(arrivals_file, 'r') as infile_arrivals:
|
||||
arrivals = infile_arrivals.readlines()
|
||||
|
||||
count = 0
|
||||
for src_number, source in sources_dict.items():
|
||||
if source['phase'] in exclude_phases:
|
||||
continue
|
||||
src_name = events[src_number - 1]
|
||||
if not src_name in event_names.keys():
|
||||
arrivals_taup[src_name] = []
|
||||
arrivals_tomo[src_name] = []
|
||||
receiver_ids[src_name] = []
|
||||
event_names[src_name] = count
|
||||
count += 1
|
||||
|
||||
for line in arrivals:
|
||||
# read line by line from fmtomo_tools output file arrivals.dat
|
||||
rec_id, src_id, ray_id, refl, arrival_time, diff, head = line.split()
|
||||
arrival_time = float(arrival_time)
|
||||
rec_id = int(rec_id)
|
||||
src_id = int(src_id)
|
||||
ray_id = int(ray_id)
|
||||
|
||||
# identify receiver and source using dictionary
|
||||
receiver = receivers_dict[rec_id]
|
||||
source = sources_dict[src_id]
|
||||
src_name = events[src_id - 1]
|
||||
phase = source['phase']
|
||||
if phase in exclude_phases: continue
|
||||
taup_arrival = model.get_travel_times_geo(source_depth_in_km=source['depth'],
|
||||
source_latitude_in_deg=source['lat'],
|
||||
source_longitude_in_deg=source['lon'],
|
||||
receiver_latitude_in_deg=receiver['lat'],
|
||||
receiver_longitude_in_deg=receiver['lon'],
|
||||
phase_list=[phase])
|
||||
receiver_depth_in_km = 6371. - receiver['rad']
|
||||
if len(taup_arrival) == 1:
|
||||
taup_arrival_time = taup_arrival[0].time
|
||||
else:
|
||||
taup_arrival_time = np.nan
|
||||
arrivals_taup[src_name].append(taup_arrival_time)
|
||||
arrivals_tomo[src_name].append(arrival_time)
|
||||
receiver_ids[src_name].append(rec_id)
|
||||
|
||||
#plt.plot([min(arrivals_taup),max(arrivals_taup)],[min(arrivals_taup), max(arrivals_taup)], 'k-')
|
||||
sorted_by_first_arrival = sorted([(src_name, min(arrivals)) for src_name, arrivals in arrivals_taup.items()],
|
||||
key=lambda x: x[1])
|
||||
|
||||
# print some output for analysis
|
||||
for item in sorted_by_first_arrival:
|
||||
print(item)
|
||||
#[print(source) for source in sources_dict.items()]
|
||||
#[print(item) for item in enumerate(events)]
|
||||
|
||||
current_fmtomo_folder_name = os.path.split(os.path.abspath(arrivals_file))[-2]
|
||||
fname_savefig = '{}'.format(current_fmtomo_folder_name)
|
||||
if exclude_phases:
|
||||
fname_savefig += '_e'
|
||||
for phase in exclude_phases:
|
||||
fname_savefig += '_{}'.format(phase)
|
||||
|
||||
plot_differences(arrivals_taup, arrivals_tomo, sorted_by_first_arrival, fname_savefig)
|
||||
#for event_name in ['20160124_103037.a_P.ttf', '20160729_211833.a_Pdiff.ttf', '20160729_211833.a_P.ttf']:
|
||||
# plot_event(arrivals_tomo, arrivals_taup, receiver_ids, receivers_dict, src_name=event_name)
|
||||
|
||||
|
||||
def plot_differences(arrivals_taup, arrivals_tomo, sorted_by_first_arrival, fname_savefig):
|
||||
fig = plt.figure(figsize=(16,9))
|
||||
ax = fig.add_subplot(111)
|
||||
|
||||
# init plot for tt differences
|
||||
cmap = plt.get_cmap('jet')
|
||||
colors = cmap(np.linspace(0, 1, len(sorted_by_first_arrival)))
|
||||
|
||||
for index, item in enumerate(sorted_by_first_arrival):
|
||||
src_name = item[0]
|
||||
ax.scatter(arrivals_taup[src_name], np.array(arrivals_tomo[src_name]) - np.array(arrivals_taup[src_name]),
|
||||
c=colors[index], s=25, marker='.', label=src_name, edgecolors='none')
|
||||
|
||||
# shrink box for legend
|
||||
box = ax.get_position()
|
||||
ax.set_position([box.x0, box.y0, box.width * 0.8, box.height])
|
||||
|
||||
ax.legend(bbox_to_anchor=[1, 1], loc='upper left')
|
||||
|
||||
plt.title(fname_savefig)
|
||||
ax.set_xlabel('Absolute time $t_{tau-p}$')
|
||||
ax.set_ylabel('Time difference $t_{hybrid} - t_{tau-p}$')
|
||||
|
||||
print('Saving plot to {}.png'.format(fname_savefig))
|
||||
fig.savefig(fname_savefig + '.png', dpi=300)
|
||||
#plt.show()
|
||||
|
||||
|
||||
def plot_event(arrivals_tomo, arrivals_taup, receiver_ids_dict, receivers_dict, src_name):
|
||||
arrivals_diff = np.array(arrivals_tomo[src_name]) - np.array(arrivals_taup[src_name])
|
||||
receiver_ids = receiver_ids_dict[src_name]
|
||||
x = np.array([receivers_dict[rec_id]['lon'] for rec_id in receiver_ids])
|
||||
y = np.array([receivers_dict[rec_id]['lat'] for rec_id in receiver_ids])
|
||||
sc = plt.scatter(x, y, c=arrivals_diff, edgecolor='none')
|
||||
plt.xlabel('Longitude [deg]')
|
||||
plt.ylabel('Latitude [deg]')
|
||||
cbar = plt.colorbar(sc)
|
||||
cbar.ax.set_ylabel('traveltime difference ($t_{hybrid} - t_{tau-p}$)')
|
||||
plt.title('{}'.format(src_name))
|
||||
plt.show()
|
||||
|
||||
# folders=[
|
||||
# 'alparray_0_receiver_elev_zero',
|
||||
# 'alparray_0_receiver_elev_zero_finer_pgrid_vgrid_r',
|
||||
# 'alparray_0_receiver_elev_zero_finer_vgrid_llr',
|
||||
# 'alparray_0_receiver_elev_zero_smaller_box',
|
||||
# 'alparray_0_receiver_elev_zero_shallow_box',
|
||||
# 'alparray_0_receiver_elev_zero_finer_pgrid_llr',
|
||||
# 'alparray_0_receiver_elev_zero_finer_interface',
|
||||
# #'alparray_0_receiver_elev_zero_bigger_box',
|
||||
# ]
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description='Compare arrivals with TauP-times')
|
||||
parser.add_argument('fmtomodir', help='path containing fm3d output')
|
||||
parser.add_argument('-e', dest='exclude', default=[], help='exclude phases, comma separated, no spaces')
|
||||
args = parser.parse_args()
|
||||
|
||||
fdir = args.fmtomodir
|
||||
|
||||
arrivals_file = os.path.join(fdir, 'arrivals.dat')
|
||||
receivers_file = os.path.join(fdir, 'receivers.in')
|
||||
sources_file = os.path.join(fdir, 'sources.in')
|
||||
input_source_file = os.path.join(fdir, 'input_source_file_P.in')
|
||||
|
||||
exclude_phases = args.exclude
|
||||
if exclude_phases:
|
||||
exclude_phases = exclude_phases.split(',')
|
||||
compare_arrivals(arrivals_file, receivers_file, sources_file, input_source_file=input_source_file, exclude_phases=exclude_phases)
|
||||
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
import os
|
||||
|
||||
cwdir = '/data/AlpArray_Data/fmtomo/v5/tradeoff_curves'
|
||||
parent_dir_name = 'crust_included_grad_smooth_FIXED_dts'#_grad_1.5'
|
||||
|
||||
dampings = [3., 10., 30.]#, 30.]
|
||||
smoothings = [5.6]
|
||||
|
||||
def main(submit_run=True):
|
||||
fdir_parent = os.path.join(cwdir, parent_dir_name)
|
||||
for damp in dampings:
|
||||
for smooth in smoothings:
|
||||
fdir_out = fdir_parent + '_sm{}_damp{}'.format(smooth, damp)
|
||||
if not os.path.isdir(fdir_out):
|
||||
os.mkdir(fdir_out)
|
||||
os.system('cp -P {}/* {}'.format(fdir_parent, fdir_out))
|
||||
invertfile = os.path.join(fdir_out, 'invert3d.in')
|
||||
modify_invert_in(invertfile, damp, smooth)
|
||||
if submit_run:
|
||||
os.chdir(fdir_out)
|
||||
os.system('qsub submit_fmtomo.sh')
|
||||
|
||||
def modify_invert_in(fnin, damp, smooth):
|
||||
with open(fnin, 'r') as infile:
|
||||
lines = infile.readlines()
|
||||
|
||||
with open(fnin, 'w') as outfile:
|
||||
for line in lines:
|
||||
if not line.startswith('c'):
|
||||
value, comment = line.split('c:')
|
||||
if 'Global damping' in comment:
|
||||
line = line.replace(value.strip(), str(damp) + ' ')
|
||||
elif 'Global smoothing' in comment:
|
||||
line = line.replace(value.strip(), str(smooth) + ' ')
|
||||
outfile.write(line)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -1,29 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import argparse
|
||||
|
||||
from obspy.geodetics.base import gps2dist_azimuth
|
||||
|
||||
|
||||
def main(infile):
|
||||
per = 4e4 # earth perimeter
|
||||
|
||||
fid = open(infile, 'r')
|
||||
nrec = fid.readline()
|
||||
latsrc, lonsrc, depsrc = [float(value) for value in fid.readline().split()]
|
||||
phase = fid.readline()
|
||||
latrec, lonrec, deprec = [float(value) for value in fid.readline().split()[:3]]
|
||||
|
||||
print ('Lat/Lon Source: {} / {}'.format(latsrc, lonsrc))
|
||||
print ('Lat/Lon Receiver: {} / {}'.format(latrec, lonrec))
|
||||
|
||||
dist_deg = gps2dist_azimuth(latsrc, lonsrc, latrec, lonrec)[0] / 1e3 / per * 360
|
||||
print ('Distance: {} [deg]'.format(dist_deg))
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser('Estimate distance from source to first'
|
||||
' receiver in WGS84 ellipsoid in FMTOMO pick file.')
|
||||
parser.add_argument('infile', help='FMTOMO pickfile (*.ttf)')
|
||||
args = parser.parse_args()
|
||||
main(args.infile)
|
||||
@@ -1,185 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import glob, os, shutil
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
from matplotlib.patches import Rectangle
|
||||
from matplotlib.collections import PatchCollection
|
||||
|
||||
from obspy.geodetics import gps2dist_azimuth
|
||||
|
||||
#pwd = '/rscratch/minos13/marcel/fmtomo_alparray/v3.5/alparray_events_thinned/picks'
|
||||
pwd = '/data/AlpArray_Data/fmtomo/v6/crust_incl_hf_sm_FIX_DTS_grad_sm30_dm10_EASI_test_Plomerova_NS_events/picks'
|
||||
os.chdir(pwd)
|
||||
infiles = glob.glob('*.ttf')
|
||||
|
||||
clat=46.
|
||||
clon=11.
|
||||
|
||||
ddist = 5
|
||||
dazim = 5
|
||||
|
||||
|
||||
def make_grid(ddist, dazim):
|
||||
distgrid = np.arange(35, 135, ddist)
|
||||
bazimgrid = np.arange(0, 360, dazim)
|
||||
grid = []
|
||||
|
||||
for bazim in np.deg2rad(bazimgrid):
|
||||
for dist in distgrid:
|
||||
grid.append(np.array([bazim, dist]))
|
||||
return np.array(grid)
|
||||
|
||||
|
||||
def make_axes():
|
||||
fig = plt.figure(figsize=(16,9))
|
||||
ax1 = fig.add_subplot(121, projection='polar')
|
||||
ax2 = fig.add_subplot(122)
|
||||
ax1.set_theta_direction(-1)
|
||||
ax1.set_theta_zero_location('N')
|
||||
return ax1, ax2
|
||||
|
||||
|
||||
def load_events():
|
||||
events = {}
|
||||
for infile in infiles:
|
||||
with open(infile, 'r') as fid:
|
||||
eventid = infile.split('.ttf')[0]
|
||||
npicks = int(fid.readline())
|
||||
lat, lon, depth = [float(item) for item in fid.readline().split()]
|
||||
dist, bazim, azim = gps2dist_azimuth(clat, clon, lat, lon, a=6.371e6, f=0)
|
||||
bazim = np.deg2rad(bazim)
|
||||
dist = dist / (np.pi * 6371) * 180 / 1e3
|
||||
events[eventid] = dict(dist=dist, bazim=bazim, npicks=npicks)
|
||||
return events
|
||||
|
||||
|
||||
def get_events_in_grid():
|
||||
events_in_grid = []
|
||||
for index, gcorner in enumerate(grid):
|
||||
bazim_l, dist_l = gcorner
|
||||
bazim_u = bazim_l + np.deg2rad(dazim)
|
||||
dist_u = dist_l + ddist
|
||||
events_in_grid.append(dict(bazims=(bazim_l, bazim_u), dists=(dist_l, dist_u), events=[]))
|
||||
for eventid, event in events.items():
|
||||
if (dist_l <= event['dist'] < dist_u) and (bazim_l <= event['bazim'] <= bazim_u):
|
||||
events_in_grid[index]['events'].append(eventid)
|
||||
return events_in_grid
|
||||
|
||||
|
||||
def filter_events():
|
||||
filtered_events = {}
|
||||
for eventdict in events_in_grid:
|
||||
cur_events = eventdict['events']
|
||||
if not cur_events: continue
|
||||
eventid = get_best_event(cur_events)
|
||||
filtered_events[eventid] = events[eventid]
|
||||
return filtered_events
|
||||
|
||||
|
||||
def get_best_event(cur_events):
|
||||
''' return eventid with highest number of picks'''
|
||||
select_events = {key: events[key] for key in cur_events}
|
||||
npicks = {key: value['npicks'] for key, value in select_events.items()}
|
||||
eventid = max(npicks, key=npicks.get)
|
||||
return eventid
|
||||
|
||||
|
||||
def plot_distribution(events_dict):
|
||||
cmap_bnd = plt.get_cmap('Greys_r')
|
||||
cmap_center = plt.get_cmap('viridis')
|
||||
nevents = [len(grid_dict['events']) for grid_dict in events_dict]
|
||||
npicks = []
|
||||
for ev_dict in events_dict:
|
||||
npick = 0
|
||||
for eventid in ev_dict['events']:
|
||||
npick += events[eventid]['npicks']
|
||||
npicks.append(npick)
|
||||
|
||||
npicks = np.array(npicks)
|
||||
|
||||
ev_max = max(nevents)
|
||||
np_max = max(npicks)
|
||||
|
||||
print('N picks total:', np.sum(npicks))
|
||||
print('N picks max: ', np_max)
|
||||
print('N events max: ', ev_max)
|
||||
|
||||
ax_polar, ax_hist = make_axes()
|
||||
patches = []
|
||||
for npick, ev_dict in zip(npicks, events_dict):
|
||||
bazim_l, bazim_u = ev_dict.get('bazims')
|
||||
dist_l, dist_u = ev_dict.get('dists')
|
||||
n_ev = len(ev_dict.get('events'))
|
||||
color_edge = cmap_bnd(n_ev / ev_max)
|
||||
color_center = cmap_center(npick / np_max)
|
||||
# color = cmap(np.random.rand())
|
||||
rect = Rectangle((bazim_l, dist_l), np.deg2rad(dazim), ddist, edgecolor=color_edge)#, facecolor=color_center)
|
||||
patches.append(rect)
|
||||
|
||||
collection = PatchCollection(patches, cmap=cmap_center)
|
||||
collection.set_array(npicks)
|
||||
ax_polar.add_collection(collection)
|
||||
ax_polar.set_ylim((10, 135))
|
||||
cbar = plt.colorbar(collection)
|
||||
cbar.set_label('N picks')
|
||||
|
||||
# ax.scatter(grid[:, 0] + 0.5 * dazim, grid[:, 1] + 0.5 * ddist, c=nevents, s=50)
|
||||
bazims = []
|
||||
dists = []
|
||||
for event in events.values():
|
||||
bazims.append(event.get('bazim'))
|
||||
dists.append(event.get('dist'))
|
||||
|
||||
ax_polar.scatter(bazims, dists, c='k', zorder=3, s=5, alpha=0.5)
|
||||
ax_hist.hist(np.rad2deg(bazims), bins=np.arange(0, 360, dazim))
|
||||
ax_hist.set_xlabel('Backazimuth (deg)')
|
||||
ax_hist.set_ylabel('Number of events')
|
||||
plt.title('Polar event distribution and histogram of backazimuths')
|
||||
|
||||
|
||||
def export_filtered_events(fdir_save='picks_save'):
|
||||
if not os.path.isdir(fdir_save):
|
||||
os.mkdir(fdir_save)
|
||||
for infile in infiles:
|
||||
eventid = infile.split('.ttf')[0]
|
||||
if not eventid in events:
|
||||
for fname in glob.glob('{}.*'.format(eventid)):
|
||||
shutil.move(fname, fdir_save)
|
||||
print('Moved file {} to path {}'.format(fname, fdir_save))
|
||||
|
||||
|
||||
def write_input_source_file(fname='input_source_file_P_new.in'):
|
||||
with open(fname, 'w') as outfile:
|
||||
outfile.write('{}\n'.format(len(events)))
|
||||
for eventid in sorted(list(events.keys())):
|
||||
outfile.write('1 1 {}.ttf\n'.format(eventid))
|
||||
|
||||
|
||||
def filter_bazim(events, bazims_list):
|
||||
events_filtered = {}
|
||||
for eventid, event_dict in events.items():
|
||||
for baz_min, baz_max in bazims_list:
|
||||
if baz_min <= event_dict['bazim'] * 180. <= baz_max:
|
||||
events_filtered[eventid] = event_dict
|
||||
|
||||
return events_filtered
|
||||
|
||||
|
||||
filter_bazims = [(330, 360), (0, 30), (150, 210)]
|
||||
|
||||
events = load_events()
|
||||
#plot_distribution(events)
|
||||
|
||||
events = filter_bazim(events, bazims_list=filter_bazims)
|
||||
print()
|
||||
#grid = make_grid(ddist, dazim)
|
||||
#events_in_grid = get_events_in_grid()
|
||||
#plot_distribution()
|
||||
#events = filter_events()
|
||||
#events_in_grid = get_events_in_grid()
|
||||
#plot_distribution(events)
|
||||
#plt.show()
|
||||
export_filtered_events()
|
||||
write_input_source_file()
|
||||
@@ -1,28 +0,0 @@
|
||||
import os
|
||||
|
||||
import numpy as np
|
||||
from obspy.geodetics import gps2dist_azimuth
|
||||
|
||||
from fmtomo_tools.fmtomo_teleseismic_utils import organize_sources
|
||||
|
||||
fmtomodir = '/data/AdriaArray_Data/fmtomo_adriaarray/alpadege/no_crust_correction'
|
||||
clon = 17.5
|
||||
clat = 42.25
|
||||
|
||||
|
||||
os.chdir(fmtomodir)
|
||||
|
||||
sources = organize_sources('sources.in')
|
||||
|
||||
dists = []
|
||||
lats = []
|
||||
lons = []
|
||||
|
||||
for source_id, source_dict in sources.items():
|
||||
slat = source_dict['lat']
|
||||
slon = source_dict['lon']
|
||||
lats.append(slat)
|
||||
lons.append(slon)
|
||||
dist_m = gps2dist_azimuth(slat, slon, clat, clon, a=6.371e6, f=0)[0]
|
||||
dist = dist_m / (np.pi * 6371) * 180 / 1e3
|
||||
dists.append(dist)
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,52 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
import os
|
||||
import argparse
|
||||
|
||||
from fmtomo_teleseismic_utils import setup_fmtomo_sim
|
||||
|
||||
#setup_fmtomo_sim('/data/AlpArray_Data/dmt_database/', '//data/AlpArray_Data/fmtomo/v6_S/crust_incl_hf_sm_FIX_DTS_grad_sm30_dm10')
|
||||
#setup_fmtomo_sim('/rscratch/minos13/marcel/dmt_database_test_event', '/home/marcel/marcel_scratch/alparray/fmtomo_traveltime_tomo/alparray_0/')
|
||||
#setup_fmtomo_sim('/rscratch/minos13/marcel/dmt_database_m7', '/home/marcel/marcel_scratch/alparray/fmtomo_traveltime_tomo/alparray_0/')
|
||||
#setup_fmtomo_sim('/rscratch/minos22/marcel/dmt_database_m7', '/rscratch/minos22/marcel/alparray/fmtomo_traveltime_tomo/alparray_0_receiver_elev_zero_smaller_box')
|
||||
#setup_fmtomo_sim('/rscratch/minos22/marcel/dmt_database_m7', '/rscratch/minos22/marcel/alparray/fmtomo_traveltime_tomo/alparray_0_receiver_elev_zero_bigger_box')
|
||||
#setup_fmtomo_sim('/rscratch/minos22/marcel/dmt_database_m7', '/rscratch/minos22/marcel/alparray/fmtomo_traveltime_tomo/alparray_0_receiver_elev_zero_shallow_box')
|
||||
#setup_fmtomo_sim('/rscratch/minos22/marcel/dmt_database_m7', '/rscratch/minos22/marcel/alparray/fmtomo_traveltime_tomo/alparray_0_receiver_elev_zero_finer_interface')
|
||||
#setup_fmtomo_sim('/rscratch/minos22/marcel/dmt_database_m7', '/rscratch/minos22/marcel/alparray/fmtomo_traveltime_tomo/alparray_0_receiver_elev_zero_bigger_box_equal_dist_2')
|
||||
|
||||
#pgrid = Propgrid('/rscratch/minos22/marcel/alparray/fmtomo_traveltime_tomo/alparray_0/propgrid.in')
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description='Prepare grid for fm3d teleseismic hybrid calculation.')
|
||||
parser.add_argument('dmt_path', help='path containing dmt_database with PyLoT picks')
|
||||
parser.add_argument('fmtomodir', help='path containing fm3d output')
|
||||
parser.add_argument('fname_extension', help='filename extension of pick files')
|
||||
parser.add_argument('--blacklist', default=None,
|
||||
help='station blacklist file (csv). After first line: NW_id,ST_id in each line')
|
||||
parser.add_argument('--no_write_init_nodes', default=False, action='store_true',
|
||||
help='do not calculate and write init nodes')
|
||||
parser.add_argument('--no_recalculate_init_nodes', default=False, action='store_true',
|
||||
help='do not recalculate init nodes if nodes file exists')
|
||||
parser.add_argument('-n', dest='ncores', default=None, help='number of cores for multiprocessing')
|
||||
parser.add_argument('--model', default='ak135')
|
||||
parser.add_argument('--s_phase', default=False, action='store_true')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
database_path = os.path.abspath(args.dmt_path)
|
||||
fmtomodir = os.path.abspath(args.fmtomodir)
|
||||
if args.ncores is not None:
|
||||
ncores = int(args.ncores)
|
||||
else:
|
||||
ncores = None
|
||||
fname_extension = args.fname_extension
|
||||
|
||||
sub_phases = {'P': ['P', 'PKP'], 'S': ['S', 'SKS']}
|
||||
phase_types = ['P']
|
||||
if args.s_phase:
|
||||
phase_types.append('S')
|
||||
|
||||
setup_fmtomo_sim(database_path, fmtomodir, fname_extension, sub_phases, ncores=ncores, check_notesfile=False,
|
||||
model=args.model, fname_station_blacklist=args.blacklist,
|
||||
no_write_init_nodes=args.no_write_init_nodes,
|
||||
no_recalculate_init_nodes=args.no_recalculate_init_nodes, phase_types=phase_types)
|
||||
@@ -1,930 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
import subprocess
|
||||
import warnings
|
||||
import os
|
||||
import glob
|
||||
from datetime import datetime
|
||||
|
||||
import multiprocessing
|
||||
import numpy as np
|
||||
import json
|
||||
|
||||
from obspy import read_events, UTCDateTime
|
||||
from obspy.taup import TauPyModel
|
||||
from obspy.geodetics import locations2degrees, gps2dist_azimuth
|
||||
|
||||
from pylot.core.util.utils import identifyPhaseID
|
||||
from pylot.core.util.obspyDMT_interface import qml_from_obspyDMT
|
||||
from pylot.tomography.utils import pol2cart, pol2cart_vector
|
||||
from pylot.tomography.utils import get_metadata
|
||||
|
||||
|
||||
class Propgrid(object):
|
||||
'''
|
||||
small class that is built from an fm3d propgrid.in file; generates a regular grid in the same way as fm3d
|
||||
'''
|
||||
def __init__(self, filename):
|
||||
self.r_earth = 6371. # earth radius in km
|
||||
self.init_propgrid(filename)
|
||||
|
||||
def init_propgrid(self, filename_propgrid):
|
||||
self.read_propgrid(filename_propgrid)
|
||||
self.init_rGrid()
|
||||
self.init_latGrid()
|
||||
self.init_longGrid()
|
||||
|
||||
def init_rGrid(self):
|
||||
self.rs = np.zeros(self.nR)
|
||||
self.rbot = self.r_earth + self.zTop - (self.nR - 1) * self.deltaR
|
||||
for index in range(self.nR):
|
||||
self.rs[index] = self.rbot + index * self.deltaR
|
||||
|
||||
def init_latGrid(self):
|
||||
self.lats = np.zeros(self.nLat)
|
||||
self.latS = np.deg2rad(self.latSdeg)
|
||||
self.deltaLat = np.deg2rad(self.deltaLatDeg)
|
||||
for index in range(self.nLat):
|
||||
self.lats[index] = self.latS + index * self.deltaLat
|
||||
|
||||
def init_longGrid(self):
|
||||
self.longs = np.zeros(self.nLong)
|
||||
self.longW = np.deg2rad(self.longWdeg)
|
||||
self.deltaLong = np.deg2rad(self.deltaLongDeg)
|
||||
for index in range(self.nLong):
|
||||
self.longs[index] = self.longW + index * self.deltaLong
|
||||
|
||||
def read_propgrid(self, filename_propgrid):
|
||||
infile = open(filename_propgrid, 'r')
|
||||
self.nR, self.nLat, self.nLong = [int(value) for value in infile.readline().split()]
|
||||
self.deltaR, self.deltaLatDeg, self.deltaLongDeg = [np.float64(value) for value in infile.readline().split()]
|
||||
self.zTop, self.latSdeg, self.longWdeg = [np.float64(value) for value in infile.readline().split()]
|
||||
infile.close()
|
||||
|
||||
|
||||
def check_event_notes(eventdir):
|
||||
eventfile = os.path.join(eventdir, 'notes.txt')
|
||||
if os.path.isfile(eventfile):
|
||||
with open(eventfile, 'r') as infile:
|
||||
notes = infile.readline()
|
||||
print(notes)
|
||||
if 'exclude' in notes:
|
||||
print('Will exclude this event!')
|
||||
return False
|
||||
else:
|
||||
print('No notes file found.')
|
||||
return True
|
||||
|
||||
|
||||
def prepare_fmtomo_dir_first_run(fmtomo_dir):
|
||||
"""
|
||||
This helper function calls the file 'fm3d_prepare_tele' in the fmtomo binary after creating a "fake" receivers.in
|
||||
and sources.in file so that fm3d can prepare the teleseismic run and writes the file "init_nodes.out" that contains
|
||||
information on the boundary nodes of the current propagation grid. Make sure that "grid3dg" has already been called
|
||||
for this function to work.
|
||||
:param fmtomo_dir:
|
||||
:return:
|
||||
"""
|
||||
def write1(fid):
|
||||
fid.write('1\n')
|
||||
|
||||
def write_rec_file():
|
||||
""" Writes a fake receiver (has to be inside the grid) """
|
||||
with open(recfile, 'w') as fid:
|
||||
write1(fid)
|
||||
fid.write(f'0 {clat} {clon}\n')
|
||||
for _ in range(3):
|
||||
write1(fid)
|
||||
|
||||
def write_src_file():
|
||||
""" Writes a fake source """
|
||||
fakesrc_str = '''
|
||||
1
|
||||
1
|
||||
P
|
||||
0.0000 0.0000 0.0000 0.00000 0.00000 0.00000
|
||||
1
|
||||
1
|
||||
2 1
|
||||
1
|
||||
1
|
||||
'''
|
||||
|
||||
with open(srcfile, 'w') as fid:
|
||||
fid.write(fakesrc_str)
|
||||
|
||||
def get_clat_clon():
|
||||
if not os.path.isfile(propgrid_file):
|
||||
raise IOError(f'Missing file {propgrid_file} for fmtomo first run preparation.')
|
||||
|
||||
with open(propgrid_file, 'r') as fid:
|
||||
_, nlat, nlon = (int(item) for item in fid.readline().split())
|
||||
_, dlat, dlon = (float(item) for item in fid.readline().split())
|
||||
_, lat0, lon0 = (float(item) for item in fid.readline().split())
|
||||
clat = lat0 + nlat / 2 * dlat
|
||||
clon = lon0 + nlon / 2 * dlon
|
||||
return clat, clon
|
||||
|
||||
# check if binary file exists
|
||||
fmtomo_prep_tele = os.path.join(fmtomo_dir, 'fm3d_prepare_tele')
|
||||
assert os.path.isfile(fmtomo_prep_tele), f'Missing binary file {fmtomo_prep_tele}'
|
||||
|
||||
# set filenames for propgrid, sources and receivers
|
||||
propgrid_file = os.path.join(fmtomo_dir, 'propgrid.in')
|
||||
recfile = os.path.join(fmtomo_dir, 'receivers.in')
|
||||
srcfile = os.path.join(fmtomo_dir, 'sources.in')
|
||||
|
||||
# get coords from propgrid
|
||||
clat, clon = get_clat_clon()
|
||||
|
||||
# write fake source and receiver files
|
||||
write_src_file()
|
||||
write_rec_file()
|
||||
|
||||
# execute fm3d_prepare tele from local directory
|
||||
curdir = os.getcwd()
|
||||
os.chdir(fmtomo_dir)
|
||||
rval = subprocess.check_output([fmtomo_prep_tele]).decode('utf-8')
|
||||
if not rval.split('\n')[-2] == ' Finished teleseismic preparation. Stop.':
|
||||
raise ValueError('Unexpected output initialisation run.')
|
||||
|
||||
if not os.path.isfile(os.path.join(fmtomo_dir, 'init_nodes.out')):
|
||||
raise Exception('Could not create output file init_nodes.')
|
||||
os.chdir(curdir)
|
||||
|
||||
# clean up
|
||||
os.remove(srcfile)
|
||||
os.remove(recfile)
|
||||
|
||||
print('Prepare fmtomo dir first run: Success')
|
||||
|
||||
|
||||
def setup_fmtomo_sim(database_path_dmt, fmtomo_dir, fname_extension, sub_phases, ncores=None, model='ak135',
|
||||
min_picks_per_phase=10, write_vtk=True, check_notesfile=True, fname_station_blacklist=None,
|
||||
no_write_init_nodes=False, no_recalculate_init_nodes=False, phase_types=('P, S')):
|
||||
'''
|
||||
main function of this program, writes picks and input source file for FMTOMO obsdata program
|
||||
'''
|
||||
|
||||
assert os.path.isdir(database_path_dmt), 'Unrecognized directory {}'.format(database_path_dmt)
|
||||
assert os.path.isdir(fmtomo_dir), 'Unrecognized directory {}'.format(fmtomo_dir)
|
||||
|
||||
tstart = datetime.now()
|
||||
print('Starting script at {}'.format(tstart))
|
||||
print('Check notesfile set to {}'.format(check_notesfile))
|
||||
|
||||
# save means of event traveltimes to dictionary for statistical analysis
|
||||
#tt_means = {}
|
||||
|
||||
fname_fmtomo_nodes = os.path.join(fmtomo_dir, 'init_nodes.out')
|
||||
|
||||
# do first initialisation of FMTOMO to generate init_nodes file if required
|
||||
if not no_recalculate_init_nodes:
|
||||
prepare_fmtomo_dir_first_run(fmtomo_dir)
|
||||
|
||||
eventdirs = glob.glob(os.path.join(database_path_dmt, '*.?'))
|
||||
nEvents = len(eventdirs)
|
||||
# create directory that will contain the picks
|
||||
picksdir = os.path.join(fmtomo_dir, 'picks')
|
||||
if not os.path.isdir(picksdir):
|
||||
os.mkdir(picksdir)
|
||||
# track and count ALL source locations & phases (P and S) for fmtomo_tools
|
||||
associations_str = ''
|
||||
nsrc_total = 0
|
||||
# iterate over P and S to create one model each
|
||||
for phase_type in phase_types:
|
||||
sourcefile_str = ''
|
||||
nsrc = 0
|
||||
# iterate over all events in "database_path_dmt"
|
||||
for eventindex, eventdir in enumerate(eventdirs):
|
||||
print('Working on {}-picks for event {} ({}/{})'.format(phase_type, eventdir,
|
||||
eventindex + 1, len(eventdirs)))
|
||||
if check_notesfile and not check_event_notes(eventdir):
|
||||
continue
|
||||
# create ObsPy event from .pkl file in dmt eventdir
|
||||
event = get_event_obspy_dmt(eventdir)
|
||||
if len(event.origins) > 1:
|
||||
raise Exception('Ambiguous origin information for event {}'.format(event))
|
||||
origin = event.origins[0]
|
||||
# get all picks from PyLoT *.xml file
|
||||
picks = get_picks(eventdir, extension=fname_extension)
|
||||
if not picks:
|
||||
print('No picks for event {} found.'.format(eventdir))
|
||||
continue
|
||||
# remove picks for blacklisted stations
|
||||
if fname_station_blacklist:
|
||||
picks, n_deleted_blacklist = remove_blacklisted_station_picks(picks, fname_station_blacklist)
|
||||
# get metadata for current event from dmt_database
|
||||
metadata = get_metadata(eventdir)
|
||||
# get a dictionary containing coordinates for all stations
|
||||
stations_dict = metadata.get_all_coordinates()
|
||||
# catch event id
|
||||
event_id = get_event_id(eventdir)
|
||||
# map specific phases to another phase (Pdiff -> P)
|
||||
merge_phases = {'Pdiff': 'P'}
|
||||
for key, value in merge_phases.items():
|
||||
print('Will map phase {} to phase {} if present.'.format(key, value))
|
||||
# assign all picks of this event to a phase and add to sorted_picks dictionary
|
||||
sorted_picks = sort_picks_by_phase(picks, stations_dict, origin,
|
||||
sub_phases[phase_type], model, merge_phases=merge_phases)
|
||||
sorted_picks = remove_uncommon_picks(sorted_picks, min_picks_per_phase)
|
||||
# the following should not be necessary when calculating traveltimes on borders using TauPy
|
||||
#sorted_picks = translate_phase_names_TAUP(sorted_picks)
|
||||
#
|
||||
# iterate over sorted picks and write a traveltime file for each phase type
|
||||
for phase, picks_list in sorted_picks.items():
|
||||
pickfile_base = '{}_{}'.format(event_id, phase)
|
||||
pickfile_name = pickfile_base + '.ttf'
|
||||
init_nodes_name = pickfile_base + '.nodes'
|
||||
vtk_file_name = pickfile_base + '.vtk'
|
||||
pickfile = os.path.join(picksdir, pickfile_name)
|
||||
init_nodes_file = os.path.join(picksdir, init_nodes_name)
|
||||
vtk_file = os.path.join(picksdir, vtk_file_name)
|
||||
# remove source time
|
||||
picks_list = subtract_source_time(picks_list, origin.time)
|
||||
# save mean for statistical analysis
|
||||
#tt_means[pickfile_base] = mean
|
||||
# create a list with all "true" phases used for taupy to calculate boundary ttimes/ray parameters
|
||||
phases = list(set([item['true_phase'] for item in picks_list]))
|
||||
if no_write_init_nodes == False:
|
||||
if no_recalculate_init_nodes and os.path.isfile(init_nodes_file):
|
||||
print('Found previously calculated init nodes. Will not recalculate.')
|
||||
else:
|
||||
# calculate travel times and ray parameters for boundary nodes and write to file
|
||||
points_list = initNodesFm3d(fname_fmtomo_nodes, origin, model, phases, ncores=ncores)
|
||||
# in case initNodes fails and returns None continue with next phase
|
||||
if not points_list:
|
||||
print('No points list, initNodesFm3d failed for event.')
|
||||
continue
|
||||
if not write_init_points(points_list, init_nodes_file):
|
||||
print('Write init points failed for event.')
|
||||
continue
|
||||
if write_vtk==True:
|
||||
write_vtk_file(points_list, vtk_file)
|
||||
# write picks to pickfile for fm3d
|
||||
write_picks_to_pickfile(pickfile, phase, picks_list, stations_dict, origin)
|
||||
# add pickfile to sourcefile string and count number of pickfiles
|
||||
sourcefile_str += '1 1 {}\n'.format(pickfile_name)
|
||||
# add source location, phaseID and .nodes filename to association string
|
||||
source_string = '{phase} {lat} {lon} {depth} {fname}\n'
|
||||
associations_str += source_string.format(phase=phase, lat=origin.latitude,
|
||||
lon=origin.longitude, depth=origin.depth,
|
||||
fname=init_nodes_name)
|
||||
nsrc += 1
|
||||
nsrc_total += 1
|
||||
average_time = (datetime.now() - tstart) / (eventindex + 1)
|
||||
print('Average time for event (phase {}): {}'.format(phase_type, average_time))
|
||||
print('ETA for {}-events: {}'.format(phase_type, tstart + nEvents * average_time))
|
||||
|
||||
write_src_file(fmtomo_dir, phase_type, nsrc, sourcefile_str)
|
||||
|
||||
write_assc_file(fmtomo_dir, nsrc_total, associations_str)
|
||||
#write_json(tt_means, os.path.join(fmtomo_dir, 'ttmeans.json'))
|
||||
|
||||
print('Script finished! Good Bye!')
|
||||
|
||||
|
||||
def write_src_file(fmtomo_dir, phase_type, nsrc, sourcefile_str):
|
||||
# write input_source_file for obsdata
|
||||
input_source_file = open(os.path.join(fmtomo_dir, 'input_source_file_{}.in'.format(phase_type)), 'w')
|
||||
input_source_file.write('{}\n'.format(nsrc))
|
||||
input_source_file.write(sourcefile_str)
|
||||
input_source_file.close()
|
||||
|
||||
|
||||
def write_assc_file(fmtomo_dir, nsrc_total, associations_str):
|
||||
# write input_associations file for fm3d
|
||||
input_assc_file = open(os.path.join(fmtomo_dir, 'input_associations_file.in'), 'w')
|
||||
input_assc_file.write('{}\n'.format(nsrc_total))
|
||||
input_assc_file.write(associations_str)
|
||||
input_assc_file.close()
|
||||
|
||||
|
||||
def write_json(object, fname):
|
||||
with open(fname, 'w') as outfile:
|
||||
json.dump(object, outfile, sort_keys=True, indent=4)
|
||||
|
||||
|
||||
def write_vtk_file(points_list, fname):
|
||||
outfile = open(fname, 'w')
|
||||
|
||||
nPoints = len(points_list)
|
||||
|
||||
outfile.write('# vtk DataFile Version 2.0\n')
|
||||
outfile.write('FMM Init points\n')
|
||||
outfile.write('ASCII\n')
|
||||
outfile.write('DATASET POLYDATA\n')
|
||||
outfile.write('POINTS {} float\n'.format(nPoints))
|
||||
|
||||
for point in points_list:
|
||||
lat = point['pt_lat']
|
||||
lon = point['pt_lon']
|
||||
rad = point['pt_R']
|
||||
x, y, z = pol2cart(lat, lon, rad)
|
||||
outfile.write('{x} {y} {z}\n'.format(x=x, y=y, z=z))
|
||||
|
||||
# write number of vertices and their indices
|
||||
outfile.write('\nVERTICES {} {}\n'.format(nPoints, 2*nPoints))
|
||||
for index, point in enumerate(points_list):
|
||||
outfile.write('{} {}\n'.format(1, index))
|
||||
|
||||
# write header with number of data points
|
||||
outfile.write('\nPOINT_DATA {}\n'.format(nPoints))
|
||||
|
||||
# write header for traveltimes
|
||||
outfile.write('SCALARS traveltime float 1\n')
|
||||
outfile.write('LOOKUP_TABLE default\n')
|
||||
|
||||
for point in points_list:
|
||||
time = point.get('time')
|
||||
outfile.write('{}\n'.format(time if time else 0))
|
||||
|
||||
# write header for point indices
|
||||
outfile.write('SCALARS point_index integer 1\n')
|
||||
outfile.write('LOOKUP_TABLE default\n')
|
||||
|
||||
for point in points_list:
|
||||
pt_index = point.get('pt_index')
|
||||
outfile.write('{}\n'.format(pt_index if pt_index else 0))
|
||||
|
||||
outfile.write('VECTORS tt_grad float\n')
|
||||
for point in points_list:
|
||||
lat = point['pt_lat']
|
||||
lon = point['pt_lon']
|
||||
r_comp = point.get('ray_param_km_z_comp')
|
||||
n_comp = point.get('ray_param_km_n_comp')
|
||||
e_comp = point.get('ray_param_km_e_comp')
|
||||
sx, sy, sz = pol2cart_vector(lat, lon, n_comp, e_comp, r_comp)
|
||||
outfile.write('{sx} {sy} {sz}\n'.format(sx=sx if sx else 0.,
|
||||
sy=sy if sy else 0.,
|
||||
sz=sz if sz else 0.,))
|
||||
|
||||
outfile.close()
|
||||
|
||||
|
||||
|
||||
|
||||
def write_init_points(points_list, outfile):
|
||||
fid = open(outfile, 'w')
|
||||
print('Writing {} init points to file {}.'.format(len(points_list), outfile))
|
||||
output_str = ''
|
||||
# count number of points for file header
|
||||
npoints = 0
|
||||
for point_dict in points_list:
|
||||
nArrivals = point_dict.get('nArrivals')
|
||||
if not nArrivals:
|
||||
#print('No arrivals for point:')
|
||||
#print(point_dict)
|
||||
continue
|
||||
if nArrivals > 1:
|
||||
fid.close()
|
||||
os.remove(outfile)
|
||||
warning_template = 'Ambiguous information for point: {}, having {} different arrivals. Skipping event.'
|
||||
print(warning_template.format(point_dict, nArrivals))
|
||||
return False
|
||||
output_template = '{index} {time} {ray_param_z} {ray_param_n} {ray_param_e}\n'
|
||||
output_str += (output_template.format(index=point_dict['pt_index'],
|
||||
time=point_dict.get('time'),
|
||||
ray_param_z=point_dict.get('ray_param_km_z_comp'),
|
||||
ray_param_n=point_dict.get('ray_param_km_n_comp'),
|
||||
ray_param_e=point_dict.get('ray_param_km_e_comp')))
|
||||
npoints += 1
|
||||
fid.write('{}\n'.format(npoints))
|
||||
fid.write(output_str)
|
||||
fid.close()
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def initNodesFm3d(filename_init_nodes, source_origin, model, phases, min_dist=25., ncores=None, R=6371000.):
|
||||
# This function uses obspy TauPy to calculate travel times and ray parameters on boundary points of the fm3d grid
|
||||
|
||||
def exposed_sides(grid_boundaries, src_lat, src_lon):
|
||||
# check which sides of grid are exposed to source (taken from fm3d teleseismic.f90 code)
|
||||
# here: baz is calculated, not az
|
||||
north = grid_boundaries['north']
|
||||
east = grid_boundaries['east']
|
||||
south = grid_boundaries['south']
|
||||
west = grid_boundaries['west']
|
||||
|
||||
# north
|
||||
baz1 = gps2dist_azimuth(north, west, src_lat, src_lon, a=R, f=0)[1]
|
||||
baz2 = gps2dist_azimuth(north, east, src_lat, src_lon, a=R, f=0)[1]
|
||||
exposed_north = all(np.cos(np.deg2rad(baz)) > 0.01 for baz in [baz1, baz2])
|
||||
# east
|
||||
baz1 = gps2dist_azimuth(north, east, src_lat, src_lon, a=R, f=0)[1]
|
||||
baz2 = gps2dist_azimuth(south, east, src_lat, src_lon, a=R, f=0)[1]
|
||||
exposed_east = all(np.sin(np.deg2rad(baz)) > 0.01 for baz in [baz1, baz2])
|
||||
# south
|
||||
baz1 = gps2dist_azimuth(south, west, src_lat, src_lon, a=R, f=0)[1]
|
||||
baz2 = gps2dist_azimuth(south, east, src_lat, src_lon, a=R, f=0)[1]
|
||||
exposed_south = all(np.cos(np.deg2rad(baz)) < -0.01 for baz in [baz1, baz2])
|
||||
# west
|
||||
baz1 = gps2dist_azimuth(north, west, src_lat, src_lon, a=R, f=0)[1]
|
||||
baz2 = gps2dist_azimuth(south, west, src_lat, src_lon, a=R, f=0)[1]
|
||||
exposed_west = all(np.sin(np.deg2rad(baz)) < -0.01 for baz in [baz1, baz2])
|
||||
|
||||
exposed_dict = dict(north=exposed_north, east=exposed_east, south=exposed_south, west=exposed_west,
|
||||
rbot=True)
|
||||
|
||||
return exposed_dict
|
||||
|
||||
def point_valid(pt_lat, pt_lon, pt_R, exposed_dict, grid_boundaries, R_earth):
|
||||
# check whether point has to be active or not depending on its position to the source
|
||||
|
||||
def check_point(actual, desired, threshold=0.001):
|
||||
# checks receiver (boundary) point orientation by comparison to corner boundary points
|
||||
return abs(actual - desired) < threshold
|
||||
|
||||
# check for negative depth (above surface)
|
||||
if pt_R > R_earth:
|
||||
return False
|
||||
|
||||
# check if point belongs to bottom interface
|
||||
#if check_point(pt_R, grid_boundaries['rbot']):
|
||||
# return True
|
||||
|
||||
pt_faces_dir = {}
|
||||
pt_exposed_dir = {}
|
||||
|
||||
directions = {'north': pt_lat,
|
||||
'east': pt_lon,
|
||||
'south': pt_lat,
|
||||
'west': pt_lon,
|
||||
'rbot': pt_R,}
|
||||
|
||||
for direction, pt_lat_or_lon in directions.items():
|
||||
# check if point belongs to boundary of this direction and save to pt_faces_dir
|
||||
pt_direction_check = check_point(pt_lat_or_lon, grid_boundaries[direction])
|
||||
pt_faces_dir[direction] = pt_direction_check
|
||||
# check if point is exposed to source in this direction and save to pt_exposed_dir
|
||||
pt_exposed_dir[direction] = pt_direction_check and exposed_dict[direction]
|
||||
|
||||
# compare number of points facing source direction to the actual direction they are facing
|
||||
return sum(pt_faces_dir.values()) == sum(pt_exposed_dir.values())
|
||||
|
||||
|
||||
import datetime
|
||||
now = datetime.datetime.now()
|
||||
infile = open(filename_init_nodes, 'r')
|
||||
R_earth = 6371.
|
||||
|
||||
# read input files containing fm3d boundary points in each line in the order: lat lon radius
|
||||
init_nodes = infile.readlines()
|
||||
nPoints = len(init_nodes)
|
||||
# split lines and convert to floats except for first value which is converted to int
|
||||
init_nodes = [[float(val) if index != 0 else int(val) for index, val in enumerate(line.split())]
|
||||
for line in init_nodes]
|
||||
|
||||
grid_boundaries = dict(north=max(init_nodes, key=lambda x: x[1])[1],
|
||||
east=max(init_nodes, key=lambda x: x[2])[2],
|
||||
south=min(init_nodes, key=lambda x: x[1])[1],
|
||||
west=min(init_nodes, key=lambda x: x[2])[2],
|
||||
rbot=min(init_nodes, key=lambda x: x[3])[3],
|
||||
rtop=max(init_nodes, key=lambda x: x[3])[3])
|
||||
|
||||
# get source coordinates
|
||||
src_lat, src_lon, src_depth = source_origin.latitude, source_origin.longitude, source_origin.depth
|
||||
#src_lat = 0; src_lon = 180; src_depth=50
|
||||
|
||||
# calculate which sides are exposed to source
|
||||
exposed_dict = exposed_sides(grid_boundaries, src_lat, src_lon)
|
||||
|
||||
# iterate over all points and calculate distance. Append a dictionary to an input list for
|
||||
# multiprocessing worker for each point
|
||||
points_list = []
|
||||
for point in init_nodes:
|
||||
pt_index, pt_lat, pt_lon, pt_R = point
|
||||
pt_depth = R_earth - pt_R
|
||||
dist = locations2degrees(pt_lat, pt_lon, src_lat, src_lon)
|
||||
|
||||
# check minimum distance for this point
|
||||
if dist < min_dist:
|
||||
warnings.warn('Distance for point {} less than minimum'
|
||||
' distance ({} km). Skipping event!'.format(point, min_dist))
|
||||
return
|
||||
|
||||
# check if point is exposed to source and has to be initiated
|
||||
if not point_valid(pt_lat, pt_lon, pt_R, exposed_dict, grid_boundaries, R_earth):
|
||||
continue
|
||||
|
||||
input_dict = {'pt_index': pt_index,
|
||||
'pt_depth': pt_depth,
|
||||
'pt_R': pt_R,
|
||||
'pt_lat': pt_lat,
|
||||
'pt_lon': pt_lon,
|
||||
'src_lat': src_lat,
|
||||
'src_lon': src_lon,
|
||||
'src_depth': src_depth,
|
||||
'dist': dist,
|
||||
'model': model,
|
||||
'phases': phases,
|
||||
}
|
||||
points_list.append(input_dict)
|
||||
|
||||
print('n Points init:', len(points_list))
|
||||
#plot_points(points_list)
|
||||
|
||||
rvals = []
|
||||
pool = multiprocessing.Pool(ncores, maxtasksperchild=1000)
|
||||
for rval in pool.imap(taup_worker, points_list, chunksize=10):
|
||||
rvals.append(rval)
|
||||
pool.close()
|
||||
pool.join()
|
||||
|
||||
print('Done after {}'.format(datetime.datetime.now() - now))
|
||||
|
||||
#plot_points(points_list)
|
||||
return rvals
|
||||
|
||||
|
||||
def taup_worker(input_dict):
|
||||
# initiate model for TauP method
|
||||
model = TauPyModel(model=input_dict['model'])
|
||||
huge_time = 1e7
|
||||
try:
|
||||
arrivals = model.get_ray_paths(input_dict['src_depth'], input_dict['dist'],
|
||||
phase_list=input_dict['phases'],
|
||||
receiver_depth_in_km=input_dict['pt_depth'], )
|
||||
if len(arrivals) < 1:
|
||||
#print('No arrivals for phase {}'.format(input_dict['phase']))
|
||||
#print(input_dict)
|
||||
return input_dict
|
||||
|
||||
arr = arrivals[0]
|
||||
|
||||
input_dict['nArrivals'] = len(arrivals)
|
||||
baz = gps2dist_azimuth(input_dict['pt_lat'], input_dict['pt_lon'],
|
||||
input_dict['src_lat'], input_dict['src_lon'],
|
||||
a=6371.*1e3, f=0)[1]
|
||||
|
||||
# calculate traveltime gradient, division by R to transform from rad to km
|
||||
# multiply with -1 to get values for azimuth instead of back-azimuth
|
||||
ray_param_km_z_comp = arr.ray_param / np.tan(np.deg2rad(arr.incident_angle)) / input_dict['pt_R']
|
||||
ray_param_km_n_comp = arr.ray_param * (-1) * np.cos(np.deg2rad(baz)) / input_dict['pt_R']
|
||||
ray_param_km_e_comp = arr.ray_param * (-1) * np.sin(np.deg2rad(baz)) / input_dict['pt_R']
|
||||
input_dict['ray_param_km_z_comp'] = ray_param_km_z_comp
|
||||
input_dict['ray_param_km_n_comp'] = ray_param_km_n_comp
|
||||
input_dict['ray_param_km_e_comp'] = ray_param_km_e_comp
|
||||
|
||||
input_dict['time'] = arr.time
|
||||
|
||||
except Exception as e:
|
||||
print(e, input_dict)
|
||||
return input_dict
|
||||
|
||||
|
||||
def plot_points(points_list):
|
||||
import matplotlib.pyplot as plt
|
||||
fig = plt.figure()
|
||||
ax = fig.add_subplot(111, projection='3d')
|
||||
x = []
|
||||
y = []
|
||||
z = []
|
||||
color = np.full(len(points_list), np.nan)
|
||||
for index, points_dict in enumerate(points_list):
|
||||
x.append(points_dict['pt_lat'])
|
||||
y.append(points_dict['pt_lon'])
|
||||
z.append(points_dict['pt_depth'])
|
||||
arrivals = points_dict.get('arrivals')
|
||||
if arrivals:
|
||||
color[index] = arrivals[0].time
|
||||
ax.scatter(x, y, z, c=color)
|
||||
plt.show()
|
||||
|
||||
|
||||
def subtract_source_time(picks_list, origin_time):
|
||||
# WAS FUNCTION DEMEAN, but demean after calculating residuals using rtimes_tele in FMTOMO!!
|
||||
for phase_dict in picks_list:
|
||||
taup_time = phase_dict['taup_time']
|
||||
pick = phase_dict['pick']
|
||||
pick_time = pick.time
|
||||
ttres = pick_time - taup_time
|
||||
# I think doing this is wrong because taup times do NOT include station elevation!!! 5.3.20
|
||||
phase_dict['ttres'] = ttres
|
||||
|
||||
mean = np.mean([phase_dict['pick'].time.timestamp for phase_dict in picks_list])
|
||||
|
||||
for phase_dict in picks_list:
|
||||
phase_dict['ttres_demeaned'] = phase_dict['ttres'] - mean # see above comment
|
||||
phase_dict['abstimes'] = phase_dict['pick'].time - origin_time
|
||||
|
||||
return picks_list
|
||||
|
||||
|
||||
def translate_phase_names_TAUP(sorted_picks):
|
||||
# PKP and PKIKP are all just 'P' phases in fm3d (not very sure about this!)
|
||||
translations = {'PKIKP': 'PKP',
|
||||
'SKIKS': 'SKS',}
|
||||
for phase_name in sorted_picks.keys():
|
||||
if phase_name in translations.keys():
|
||||
sorted_picks[translations[phase_name]] = sorted_picks.pop(phase_name)
|
||||
return sorted_picks
|
||||
|
||||
|
||||
def write_picks_to_pickfile(pickfile, phase, picks_list, stations_dict, origin, picks_mode='abs'):
|
||||
fid = open(pickfile, 'w')
|
||||
header = '{npicks}\n' \
|
||||
'{lat_src:<15} {lon_src:<15} {depth_src:<15}\n' \
|
||||
'{phase_name}\n'
|
||||
# pickfile header including npicks, src coords, phasetype
|
||||
formatted_str = header.format(npicks=len(picks_list),
|
||||
lat_src=origin.latitude,
|
||||
lon_src=origin.longitude,
|
||||
depth_src=origin.depth,
|
||||
phase_name=phase)
|
||||
fid.write(formatted_str)
|
||||
|
||||
# write picks for each station to pickfile
|
||||
for phase_dict in picks_list:
|
||||
pick = phase_dict['pick']
|
||||
seed_id = pick.waveform_id.get_seed_string()
|
||||
# travel time residual (demeaned or abs)
|
||||
if picks_mode == 'abs':
|
||||
picktime = phase_dict['abstimes']
|
||||
elif picks_mode == 'res':
|
||||
warnings.warn('Using residuals here might not be exact. See above code where ttres_demeaned is calculated.')
|
||||
picktime = phase_dict['ttres_demeaned']
|
||||
else:
|
||||
raise IOError(f'Unknown picks_mode {picks_mode}')
|
||||
uncertainty = pick.time_errors.uncertainty
|
||||
network, station = seed_id.split('.')[:2]
|
||||
# get stations coords from metadata dict
|
||||
station_coords = stations_dict.get('{}.{}'.format(network, station))
|
||||
# prepare line to be written to pickfile and format, use traveltime residual
|
||||
line = '{lat_rec:<15} {lon_rec:<15} {depth_rec:<15} {picktime:<15} {uncert:15}\n'
|
||||
formatted_line = line.format(lat_rec=station_coords['latitude'],
|
||||
lon_rec=station_coords['longitude'],
|
||||
depth_rec=station_coords['elevation'] * (-1e-3),
|
||||
picktime=picktime,
|
||||
uncert=uncertainty)
|
||||
fid.write(formatted_line)
|
||||
print('Wrote {} picks for phase {} to file {}'.format(len(picks_list), phase, pickfile))
|
||||
fid.close()
|
||||
|
||||
|
||||
def remove_blacklisted_station_picks(picks, fname_blacklist, verbosity=1):
|
||||
blacklisted_stations = get_station_blacklist(fname_blacklist)
|
||||
deleted_picks = []
|
||||
deleted_stations = []
|
||||
for index, pick in list(reversed(list(enumerate(picks)))):
|
||||
seed_id = pick.waveform_id.get_seed_string()
|
||||
network, station = seed_id.split('.')[:2]
|
||||
nwst_id = '{}.{}'.format(network, station)
|
||||
if nwst_id in blacklisted_stations.keys():
|
||||
timewindow = blacklisted_stations[nwst_id]
|
||||
if not timewindow == 'always':
|
||||
tstart, tstop = [UTCDateTime(time) for time in timewindow.split('to')]
|
||||
if not tstart <= picks[index].time <= tstop:
|
||||
continue
|
||||
deleted_picks.append(picks.pop(index))
|
||||
deleted_stations.append(nwst_id)
|
||||
if verbosity > 0:
|
||||
print('Deleted picks for blacklisted stations:\n{}'.format(deleted_stations))
|
||||
return picks, deleted_stations
|
||||
|
||||
|
||||
def get_station_blacklist(fname_csv):
|
||||
with open(fname_csv, 'r') as infile:
|
||||
# skip first line
|
||||
blacklist = infile.readlines()[1:]
|
||||
blacklisted_stations = {}
|
||||
for line in blacklist:
|
||||
network, station, time = line.split(',')[:3]
|
||||
nwst_id = '{}.{}'.format(network, station)
|
||||
blacklisted_stations[nwst_id] = time
|
||||
return blacklisted_stations
|
||||
|
||||
|
||||
def remove_uncommon_picks(sorted_picks, min_picks_per_phase):
|
||||
for phase_name, picks_list in reversed(list(sorted_picks.items())):
|
||||
if len(picks_list) < min_picks_per_phase:
|
||||
msg = 'Removed picks for phase {}, as there are only {} picks given (threshold is {})'
|
||||
print(msg.format(phase_name, len(picks_list), min_picks_per_phase))
|
||||
del(sorted_picks[phase_name])
|
||||
return sorted_picks
|
||||
|
||||
|
||||
def sort_picks_by_phase(picks, stations_dict, source_origin, phase_types, model,
|
||||
max_phase_diff=50., merge_phases=None, verbosity=0):
|
||||
'''
|
||||
# First: Iterate through all picks to estimate possible phases for source/location combination, then assign
|
||||
# each pick to one phase and return sorted picks dictionary, as there has to be one pickfile for each phasetype
|
||||
|
||||
:param picks:
|
||||
:param stations_dict:
|
||||
:param source_origin:
|
||||
:param phase_types:
|
||||
:param model:
|
||||
:param max_phase_diff:
|
||||
:param merge_phases: assign a phase (key) to another phase (value) e.g. {Pdiff: P}
|
||||
:param verbosity:
|
||||
:return:
|
||||
'''
|
||||
print('Starting to sort picks by phase calculating theoretical travel times for each pick...')
|
||||
phases_dict = {}
|
||||
for pick in picks:
|
||||
# PROBLEM: seed_id from PyLoT does not contain Location ID!!! Makes it hard to find station coords
|
||||
# workaround: use stations_dict (ignore channel and location ID) instead of metadata.get_coordinates()
|
||||
seed_id = pick.waveform_id.get_seed_string()
|
||||
network, station = seed_id.split('.')[:2]
|
||||
phaseID = pick.phase_hint
|
||||
uncertainty = pick.time_errors.uncertainty
|
||||
# skip different phase
|
||||
if not phaseID in phase_types:
|
||||
continue
|
||||
# pick invalid if no uncertainty is given
|
||||
if not uncertainty:
|
||||
continue
|
||||
station_coords = stations_dict.get('{}.{}'.format(network, station))
|
||||
if not station_coords:
|
||||
print('Could not find coordinates for station: {}'.format(seed_id))
|
||||
continue
|
||||
# estimate phase type by taking the closest phase from Tau-P method (time is relative to source origin)
|
||||
phase_name, phase_time_rel, phase_diff = get_closest_taup_phase(source_origin, station_coords,
|
||||
phaseID, pick.time, model)
|
||||
if phase_diff > max_phase_diff:
|
||||
if verbosity > 0:
|
||||
print ('Warning, max_diff too large (> {} s) for phase {} at {}. Skipping'.format(max_phase_diff,
|
||||
phase_name, seed_id))
|
||||
continue
|
||||
phase_time = source_origin.time + phase_time_rel
|
||||
|
||||
# merge phase if selected, keep track of original phase for TauPy node initiation
|
||||
true_phase = phase_name
|
||||
if merge_phases and phase_name in merge_phases.keys():
|
||||
phase_name = merge_phases[phase_name]
|
||||
|
||||
if not phase_name in phases_dict.keys():
|
||||
print('Adding phase to sorted picks dictionary: {}'.format(phase_name))
|
||||
phases_dict[phase_name] = []
|
||||
phases_dict[phase_name].append({'pick': pick,
|
||||
'taup_time': phase_time,
|
||||
'true_phase': true_phase,})
|
||||
return phases_dict
|
||||
|
||||
def get_closest_taup_phase(source_origin, station_coords, phase, pick_time, model='ak135'):
|
||||
'''
|
||||
Estimate phase that was picked by searching for earliest P/S phase arriving at
|
||||
station_coords for given source_coords using TauPy. As PyLoT only searches for first arrivals
|
||||
using the same method, this should(!) yield the correct phase.
|
||||
:return:
|
||||
'''
|
||||
phases = {'P': [],
|
||||
'S': []}
|
||||
phase_lists = {'P': ['ttp'],
|
||||
'S': ['tts']}
|
||||
|
||||
# possible phases for fm3d (*K* and *KIK* -> *)
|
||||
#phase_list = {'P': ['P', 'PKP', 'PKiKP', 'PKIKP', 'PcP', 'ScP', 'SKP', 'PKKP', 'SKKP', 'PP',],
|
||||
# 'S': ['S', 'SKS', 'SKIKS', 'ScS']}
|
||||
|
||||
# in case pick phase hint is just P or S
|
||||
if phase in phases.keys():
|
||||
phase_list = phase_lists[phase]
|
||||
common_phase = phase
|
||||
# in case an explicit phase name is given use only this phase
|
||||
else:
|
||||
phase_list = [phase]
|
||||
common_phase = identifyPhaseID(phase)
|
||||
|
||||
model = TauPyModel(model=model)
|
||||
arrivals = model.get_travel_times_geo(source_origin.depth,
|
||||
source_origin.latitude,
|
||||
source_origin.longitude,
|
||||
station_coords['latitude'],
|
||||
station_coords['longitude'],
|
||||
phase_list)
|
||||
|
||||
# identifies phases from arrivals as P or S phase, not necessary when using 'ttp' or 'tts' for get_travel_times_geo
|
||||
for arr in arrivals:
|
||||
phases[identifyPhaseID(arr.phase.name)].append(arr)
|
||||
|
||||
source_time = source_origin.time
|
||||
|
||||
if not arrivals:
|
||||
raise Exception('No arrivals found for source.')
|
||||
|
||||
# get first P or S onsets from arrivals list
|
||||
arr, min_diff = min([(arr, abs(source_time + arr.time - pick_time)) for arr in phases[common_phase]],
|
||||
key=lambda t: t[1])
|
||||
return arr.name, arr.time, min_diff
|
||||
|
||||
|
||||
def get_event_obspy_dmt(eventdir):
|
||||
event_pkl_file = os.path.join(eventdir, 'info', 'event.pkl')
|
||||
if not os.path.exists(event_pkl_file):
|
||||
raise IOError('Could not find event path for event: {}'.format(eventdir))
|
||||
event = qml_from_obspyDMT(event_pkl_file)
|
||||
return event
|
||||
|
||||
|
||||
def get_event_pylot(eventdir, extension=''):
|
||||
event_id = get_event_id(eventdir)
|
||||
filename = os.path.join(eventdir, 'PyLoT_{}{}.xml'.format(event_id, extension))
|
||||
if not os.path.isfile(filename):
|
||||
return
|
||||
cat = read_events(filename)
|
||||
return cat[0]
|
||||
|
||||
def get_event_id(eventdir):
|
||||
event_id = os.path.split(eventdir)[-1]
|
||||
return event_id
|
||||
|
||||
def get_picks(eventdir, extension=''):
|
||||
event_id = get_event_id(eventdir)
|
||||
filename = 'PyLoT_{}{}.xml'
|
||||
filename = filename.format(event_id, extension)
|
||||
fpath = os.path.join(eventdir, filename)
|
||||
fpaths = glob.glob(fpath)
|
||||
if len(fpaths) == 1:
|
||||
cat = read_events(fpaths[0])
|
||||
picks = cat[0].picks
|
||||
return picks
|
||||
elif len(fpaths) == 0:
|
||||
print('get_picks: File not found: {}'.format(fpath))
|
||||
return
|
||||
print(f'WARNING: Ambiguous pick file specification. Found the following pick files {fpaths}\nFilemask: {fpath}')
|
||||
return
|
||||
|
||||
def init_sources_file(nsrc, filename='sources.in'):
|
||||
infile_source = open(filename, 'w')
|
||||
infile_source.write('{}\n'.format(nsrc))
|
||||
return infile_source
|
||||
|
||||
def init_receivers_file(nrec, filename='receivers.in'):
|
||||
infile_rec = open(filename, 'w')
|
||||
infile_rec.write('{}\n'.format(nrec))
|
||||
return infile_rec
|
||||
|
||||
def append_source(fid, origin):
|
||||
pass
|
||||
|
||||
def append_receiver(fid, station_coords):
|
||||
# TODO check if order is correct
|
||||
fid.write('{rad:15} {lat:15} {lon:15} \n'.format(rad=station_coords['elevation'],
|
||||
lat=station_coords['latitude'],
|
||||
lon=station_coords['longitude']))
|
||||
|
||||
|
||||
def organize_receivers(fnin, unique=False):
|
||||
''' Open FMTOMO receivers.in file and read position of each receiver into a dict.'''
|
||||
|
||||
with open(fnin, 'r') as infile:
|
||||
nRec = int(infile.readline())
|
||||
rec_dict = {}
|
||||
for rec_number in range(1, nRec + 1):
|
||||
rad, lat, lon = [float(value) for value in infile.readline().split()]
|
||||
# dummy read next 3 lines
|
||||
for ind in range(3):
|
||||
infile.readline()
|
||||
|
||||
receiver = {'rad': rad,
|
||||
'lat': lat,
|
||||
'lon': lon, }
|
||||
if unique:
|
||||
if receiver in rec_dict.values():
|
||||
continue
|
||||
|
||||
rec_dict[rec_number] = receiver
|
||||
|
||||
return rec_dict
|
||||
|
||||
|
||||
def organize_sources(fnin):
|
||||
''' Open FMTOMO sources.in file and read position and phase of each source into a dict.'''
|
||||
with open(fnin, 'r') as infile:
|
||||
nSrc = int(infile.readline())
|
||||
src_dict = {}
|
||||
for src_number in range(nSrc):
|
||||
src_number += 1
|
||||
teleseismic_flag = int(infile.readline())
|
||||
if teleseismic_flag != 1: raise ValueError('Source not teleseismic!')
|
||||
phase = infile.readline().strip()
|
||||
rad, lat, lon = [float(value) for value in infile.readline().split()[:3]]
|
||||
# dummy read next 4 lines
|
||||
for ind in range(4):
|
||||
infile.readline()
|
||||
src_dict[src_number] = {'phase': phase,
|
||||
'depth': rad,
|
||||
'lat': lat,
|
||||
'lon': lon,}
|
||||
return src_dict
|
||||
|
||||
|
||||
def organize_event_names(fnin):
|
||||
infile = open(fnin, 'r')
|
||||
events = [line.split()[-1].strip() for line in infile.readlines()[1:]]
|
||||
infile.close()
|
||||
return events
|
||||
|
||||
|
||||
def export_otimes(otimes, fn_out):
|
||||
np.savetxt(fn_out, otimes, fmt=['%8d', '%8d', '%8d', '%8d', '% 4.6f', '% 4.6f'],
|
||||
header=str(len(otimes)), comments='')
|
||||
print('Wrote file:', fn_out)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
# testing area
|
||||
prepare_fmtomo_dir_first_run('/data/AdriaArray_Data/fmtomo_adriaarray/v0/test_init')
|
||||
@@ -1,104 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# get traveltime residuals using synthetic 1D travel times from FMTOMO hybrid method
|
||||
#
|
||||
# this script is used to make sure that synthetic travel times are the same for synthetic data residuals (rtimes)
|
||||
# and observed data residuals, because of small differences between hybrid 1D solver and taup method
|
||||
# Therefore, correlation output times are absolute (minus source time) values and residuals are calcuated using
|
||||
# FMTOMO hybrid reference times (e.g. rtimes.dat)
|
||||
#
|
||||
import os
|
||||
import argparse
|
||||
import shutil
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
from pylot.tomography.fmtomo_tools.fmtomo_teleseismic_utils import organize_event_names
|
||||
|
||||
# Note: weighting of mean might not be reasonable due to uncertainty distribution along the array
|
||||
def main(infile_rtimes, fn_events, kde_file, demean=True, no_deref=False, weight_kde=True, weight=False):
|
||||
eventnames = organize_event_names(fn_events)
|
||||
assert not(weight and weight_kde), 'Cannot use two weights'
|
||||
|
||||
if not no_deref:
|
||||
reftimes = np.genfromtxt(infile_rtimes)
|
||||
if weight_kde:
|
||||
kde_values = np.genfromtxt(kde_file)
|
||||
assert len(kde_values) == len(reftimes), 'Missmatch in reftimes and kde file length'
|
||||
|
||||
nSrc = len(eventnames)
|
||||
|
||||
diff_in_means = []
|
||||
# iterate over all sources
|
||||
for index in range(nSrc):
|
||||
srcid = index + 1
|
||||
eventname = eventnames[index]
|
||||
print('{} ({}/{})'.format(eventname, srcid, nSrc))
|
||||
# get pick filename and read observed times
|
||||
pickfile = os.path.join('picks', eventname)
|
||||
obsdata_src = np.loadtxt(pickfile, skiprows=3)
|
||||
# make safety copy
|
||||
picksafedir = os.path.join('picks', 'save')
|
||||
if not os.path.isdir(picksafedir):
|
||||
os.mkdir(picksafedir)
|
||||
shutil.copy(pickfile, picksafedir)
|
||||
npicks = len(obsdata_src)
|
||||
# read observed times from pickfile
|
||||
tt_obs = obsdata_src[:, 3]
|
||||
# read uncertainties from pickfile
|
||||
uncs = obsdata_src[:, 4]
|
||||
|
||||
if no_deref:
|
||||
tt_res = tt_obs
|
||||
else:
|
||||
indices = np.where(reftimes[:, 1] == srcid)[0]
|
||||
assert (len(indices) == npicks), 'Missmatch in indices for srcids'
|
||||
# read reference teleseismic times from FMTOMO run
|
||||
tt_ref = reftimes[:, 4][indices]
|
||||
# calculate residuals
|
||||
tt_res = tt_obs - tt_ref
|
||||
|
||||
# set new residual to obsdata array
|
||||
obsdata_src[:, 3] = tt_res
|
||||
# get kde values
|
||||
#weights = 1./kde_values[:, 2][indices] if weight_kde else None
|
||||
#
|
||||
weights = 1. / uncs ** 2 if weight else None
|
||||
# demean residuals for current source
|
||||
mean = np.average(tt_res, weights=weights)
|
||||
mean_unweighted = np.mean(tt_res)
|
||||
diff_in_means.append(mean - mean_unweighted)
|
||||
print('Mean:', mean)
|
||||
print('Unweighted Mean:', mean_unweighted)
|
||||
print('Demean setting:', demean)
|
||||
if demean:
|
||||
obsdata_src[:, 3] -= mean
|
||||
#obsdata_src[:, 3] = mtimes[:, 4][indices]
|
||||
# Write new pickfiles
|
||||
with open(pickfile, 'r') as infile_pick:
|
||||
header = infile_pick.readlines()[:3]
|
||||
with open(pickfile, 'w') as outfile_pick:
|
||||
for line in header:
|
||||
outfile_pick.write(line)
|
||||
for line in obsdata_src:
|
||||
outfile_pick.write('{:10.4f} {:10.4f} {:10.4f} {:15.8f} {:15.8f}\n'.format(*line))
|
||||
plt.hist(diff_in_means, bins=100)
|
||||
plt.title('Diffs in means (weighted - unweighted) [s]')
|
||||
plt.show()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Calculate residuals for absolute observation times using reference file from FMTOMO')
|
||||
parser.add_argument('--rtimes', default='rtimes_tele.dat', help='reference_file')
|
||||
parser.add_argument('--sourcefile', default='input_source_file_P.in', help='input_source_file')
|
||||
#parser.add_argument('--kdefile', default='kde_weights.txt', help='input file with station kde values for weighting')
|
||||
parser.add_argument('-nd', '--no_demean', action='store_true', default=False,
|
||||
help='do not mean correct travel times')
|
||||
parser.add_argument('-nr', '--no_reftimes', action='store_true', default=False,
|
||||
help='do not use reference times (e.g. picks are already tt residuals). rtimes file still required at the moment.')
|
||||
#parser.add_argument('-nwk', '--no_weight_kde', action='store_true', default=True, help='do not weight travel times')
|
||||
args = parser.parse_args()
|
||||
#args.kdefile
|
||||
main(args.rtimes, args.sourcefile, None, not(args.no_demean), weight_kde=False, no_deref=args.no_reftimes) #not(args.no_weight))
|
||||
@@ -1,174 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
import os
|
||||
import numpy as np
|
||||
|
||||
from obspy.geodetics import gps2dist_azimuth, locations2degrees
|
||||
from pylot.tomography.utils import get_coordinate_from_dist_baz
|
||||
|
||||
R=6371.
|
||||
|
||||
def main():
|
||||
profiles_infile = '/home/marcel/AlpArray/vtk_files/points_mark.txt'
|
||||
gmtslice_dir = '/rscratch/minos13/marcel/fmtomo_alparray/v3.5/alparray_mantle_diehl_crust_included_hf_gradient_smoothing/plot'
|
||||
####
|
||||
# maximum point spacing in km (decreases with depth)
|
||||
dpoints = 1.
|
||||
dpi = 100
|
||||
model_depth = 606.
|
||||
###
|
||||
with open(profiles_infile, 'r') as infile:
|
||||
profiles = infile.readlines()
|
||||
|
||||
for profile in profiles:
|
||||
print('Profile: ', profile)
|
||||
name = profile.split()[0]
|
||||
lon1, lat1, lon2, lat2 = [float(item) for item in profile.split()[1:]]
|
||||
|
||||
|
||||
def get_gmtslice_gc(gmtplot_dir, lat1, lon1, lat2, lon2, model_depth=606., dpoints=1, fn_out_slice='grid2dvgc.z',
|
||||
rel=True, fname_vgrid='../vgrids.in', fname_vgrid_ref='../vgridsref.in'):
|
||||
parfile_str_len = 28
|
||||
assert (len(fname_vgrid) < parfile_str_len), 'Check length of filename: {}'.format(fname_vgrid)
|
||||
assert (len(fname_vgrid_ref) < parfile_str_len), 'Check length of filename: {}'.format(fname_vgrid_ref)
|
||||
|
||||
infile_default = 'gmtslice_default.in'
|
||||
fn_out = 'gmtslice.in'
|
||||
bounds_out_gmt = 'boundgc.gmt'
|
||||
|
||||
cwd = os.getcwd()
|
||||
|
||||
os.chdir(gmtplot_dir)
|
||||
with open(infile_default, 'r') as infile:
|
||||
gmt_infile = infile.readlines()
|
||||
|
||||
# calculate great circle distance
|
||||
max_dist = gps2dist_azimuth(lat1, lon1, lat2, lon2, a=R * 1e3, f=0)[0]/1e3
|
||||
npointsR = int(model_depth / dpoints + 1)
|
||||
npointsLateral = int(max_dist / dpoints + 1)
|
||||
print('nR, nLateral:', npointsR, npointsLateral)
|
||||
|
||||
# filename
|
||||
gmt_infile[3] = '{} \n'.format(fname_vgrid)
|
||||
gmt_infile[4] = '{} \n'.format(fname_vgrid_ref)
|
||||
|
||||
# activate gc generation
|
||||
gmt_infile[51] = '1 \n'
|
||||
# modify lines with lat lon boundaries
|
||||
gmt_infile[52] = '{:5.2f} {:5.2f}\n'.format(lat1, lon1)
|
||||
gmt_infile[53] = '{:5.2f} {:5.2f}\n'.format(lat2, lon2)
|
||||
|
||||
abs_rel = 1 if rel else 0
|
||||
gmt_infile[58] = '{} \n'.format(abs_rel)
|
||||
|
||||
# modify lines with n Points
|
||||
gmt_infile[65] = '{:<5d} {:<5d}\n'.format(npointsLateral, npointsR)
|
||||
|
||||
with open(fn_out, 'w') as outfile:
|
||||
for line in gmt_infile:
|
||||
outfile.write(line)
|
||||
|
||||
print('Executing gmtslice...')
|
||||
os.system('gmtslice')
|
||||
print('Done!')
|
||||
|
||||
with open(bounds_out_gmt, 'r') as infile:
|
||||
bds = [float(bd) for bd in infile.readlines()]
|
||||
|
||||
bounds = '-R{bds[0]}/{bds[1]}/{bds[2]}/{bds[3]}'.format(bds=bds)
|
||||
xyz_string = 'gmt xyz2grd {grid_out} -Ggrid2dvgc.grd -I{bds[4]}+/{bds[5]}+ -ZLB {bounds}'.format(
|
||||
grid_out=fn_out_slice, bds=bds, bounds=bounds)
|
||||
print(xyz_string)
|
||||
os.system(xyz_string)
|
||||
|
||||
grid = np.loadtxt(fn_out_slice)
|
||||
dist_grid = []
|
||||
lon_grid = []
|
||||
lat_grid = []
|
||||
_, azim, bazim = gps2dist_azimuth(lat1, lon1, lat2, lon2, a=R * 1e3, f=0)
|
||||
#ddist = / (npointsLateral - 1)
|
||||
#ddepth = (bds[3] - bds[2]) / (npointsR - 1)
|
||||
for depth in np.linspace(-bds[3], -bds[2], num=npointsR):
|
||||
for dist in np.linspace(0, locations2degrees(lat1, lon1, lat2, lon2), num=npointsLateral):
|
||||
lon, lat = get_coordinate_from_dist_baz((lon1, lat1), dist, azim)
|
||||
lon_grid.append(lon)
|
||||
lat_grid.append(lat)
|
||||
#dist = ddist * indexLat
|
||||
#depth = ddepth * indexR
|
||||
dist_grid.append(np.array([dist, depth]))
|
||||
|
||||
dist_grid = np.array(dist_grid)
|
||||
lat_grid = np.array(lat_grid)
|
||||
lon_grid = np.array(lon_grid)
|
||||
|
||||
os.chdir(cwd)
|
||||
return grid, dist_grid, lat_grid, lon_grid, max_dist
|
||||
|
||||
|
||||
def get_gmtslice_depth(gmtplot_dir, depth, fn_out_slice='grid2dvd.z', fname_vgrid='../vgrids.in',
|
||||
fname_vgrid_ref='../vgridsref.in'):
|
||||
infile_default = 'gmtslice_default.in'
|
||||
fn_out = 'gmtslice.in'
|
||||
bounds_out_gmt = 'bounddp.gmt'
|
||||
|
||||
cwd = os.getcwd()
|
||||
|
||||
os.chdir(gmtplot_dir)
|
||||
with open(infile_default, 'r') as infile:
|
||||
gmt_infile = infile.readlines()
|
||||
|
||||
# filename
|
||||
gmt_infile[3] = '{} \n'.format(fname_vgrid)
|
||||
gmt_infile[4] = '{} \n'.format(fname_vgrid_ref)
|
||||
|
||||
# activate depth slice generation
|
||||
gmt_infile[41] = '1 \n'
|
||||
# set depth (negative for whatever reason)
|
||||
gmt_infile[42] = '{:5.2f} \n'.format(-depth)
|
||||
|
||||
with open(fn_out, 'w') as outfile:
|
||||
for line in gmt_infile:
|
||||
outfile.write(line)
|
||||
|
||||
print('Executing gmtslice...')
|
||||
os.system('gmtslice')
|
||||
print('Done!')
|
||||
|
||||
with open(bounds_out_gmt, 'r') as infile:
|
||||
bds = [float(bd) for bd in infile.readlines()]
|
||||
|
||||
bounds = '-R{bds[0]}/{bds[1]}/{bds[2]}/{bds[3]}'.format(bds=bds)
|
||||
xyz_string = 'gmt xyz2grd {grid_out} -Ggrid2dvd.grd -I{bds[4]}+/{bds[5]}+ -ZLB {bounds}'.format(
|
||||
grid_out=fn_out_slice, bds=bds, bounds=bounds)
|
||||
print(xyz_string)
|
||||
#os.system(xyz_string)
|
||||
|
||||
grid = np.loadtxt(fn_out_slice)
|
||||
lonlat = []
|
||||
lon0, lon1 = bds[:2]
|
||||
lat0, lat1 = bds[2:4]
|
||||
nlon = int(bds[4])
|
||||
nlat = int(bds[5])
|
||||
for lon in np.linspace(lon0, lon1, num=nlon):
|
||||
for lat in np.linspace(lat0, lat1, num=nlat):
|
||||
lonlat.append(np.array([lon, lat]))
|
||||
|
||||
lonlat = np.array(lonlat)
|
||||
|
||||
os.chdir(cwd)
|
||||
return grid, lonlat
|
||||
|
||||
|
||||
def grdimage_slice(lat1, lon1, lat2, lon2, bounds, name, npointsLateral, npointsR, dpi):
|
||||
proj = "-JX{:05.2f}i/{:05.2f}i".format(npointsLateral / dpi, npointsR / dpi)
|
||||
fnout = 'gmtslice_{}_{:.1f}_{:.1f}-{:.1f}_{:.1f}'.format(name, lat1, lon1, lat2, lon2)
|
||||
grdimage_string = 'gmt grdimage grid2dvgc.grd {bounds} {proj} -Ba50f10/a50f10 -Cpolar_inv_3.cpt -K > {fnout}'.format(
|
||||
bounds=bounds, proj=proj, fnout=fnout+'.ps')
|
||||
print(grdimage_string)
|
||||
os.system(grdimage_string)
|
||||
print('Convert to png...')
|
||||
os.system('convert {} {}'.format(fnout + '.ps', fnout + '.png'))
|
||||
|
||||
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
import sys
|
||||
|
||||
fnin1 = '/data/AlpArray_Data/fmtomo/v6/crust_incl_hf_sm_FIX_DTS_grad_sm30_dm10/it_12/vgrids.in'
|
||||
fnin2 = '/data/AlpArray_Data/fmtomo/v6/crust_incl_hf_sm_FIX_TESAURO_grad_sm30_dm10/it_12/vgrids.in'
|
||||
fnout = '/data/AlpArray_Data/fmtomo/v6/crust_incl_hf_sm_FIX_DTS_grad_sm30_dm10/vg_TES_i12sm30dm10.in'
|
||||
|
||||
|
||||
def check_vgrids_header_line(l1, l2, epsilon=1e-6):
|
||||
items1 = [float(item) for item in l1.split()]
|
||||
items2 = [float(item) for item in l2.split()]
|
||||
for i1, i2 in zip(items1, items2):
|
||||
if not abs(i1 - i2) < epsilon:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
|
||||
def vgrids_diff(fnin1, fnin2, fnout):
|
||||
diffs = []
|
||||
with open(fnin1, 'r') as infile1:
|
||||
with open(fnin2, 'r') as infile2:
|
||||
with open(fnout, 'w') as outfile:
|
||||
for index, l1 in enumerate(infile1):
|
||||
l2 = infile2.readline()
|
||||
if index < 4:
|
||||
assert check_vgrids_header_line(l1, l2), 'Different grid dimensions!'
|
||||
outfile.write(l1)
|
||||
else:
|
||||
try:
|
||||
v1 = float(l1.split()[0])
|
||||
v2 = float(l2.split()[0])
|
||||
except Exception as e:
|
||||
print('Read problem: {}'.format(e))
|
||||
sys.exit()
|
||||
vdiff = abs(v2 - v1)
|
||||
diffs.append(vdiff)
|
||||
outfile.write('{}\n'.format(vdiff))
|
||||
|
||||
print('Finished writing file', fnout)
|
||||
print('Max diff:', max(diffs))
|
||||
|
||||
|
||||
vgrids_diff(fnin1, fnin2, fnout)
|
||||
@@ -1,126 +0,0 @@
|
||||
import os
|
||||
import json
|
||||
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
from pylot.tomography.fmtomo_tools.fmtomo_teleseismic_utils import organize_receivers
|
||||
|
||||
# TODO: demeaning source by source??
|
||||
|
||||
def calc_misfit(data, synth, unc):
|
||||
return np.sum(((data - synth) / unc) ** 2) / len(data)
|
||||
|
||||
def calc_mean_residuals(data, synth):
|
||||
return np.sum(abs(data - synth)) / len(data)
|
||||
|
||||
|
||||
def sort_stations_for_search(station_coords, receivers_dict):
|
||||
epsilon = 1e-1 # very large epsilon actually...
|
||||
coords_unique = coords_unique_receivers(receivers_dict)
|
||||
latlon_dict = {}
|
||||
for nwst_id, coords in station_coords.items():
|
||||
lat = coords['latitude']
|
||||
lon = coords['longitude']
|
||||
for latu, lonu in coords_unique:
|
||||
if abs(lat - latu) < epsilon and abs(lon - lonu) < epsilon:
|
||||
latlon_dict[(latu, lonu)] = nwst_id
|
||||
return latlon_dict
|
||||
|
||||
|
||||
def extract_stations_for_network(station_coords, network_id):
|
||||
station_coords_extracted = {}
|
||||
for nwst_id, coords in station_coords.items():
|
||||
nw, st = nwst_id.split('.')
|
||||
if nw == network_id:
|
||||
station_coords_extracted[nwst_id] = coords
|
||||
return station_coords_extracted
|
||||
|
||||
|
||||
def get_receiver_ids(latlon_list, receivers_dict):
|
||||
receiver_ids = []
|
||||
for recid, coords in receivers_dict.items():
|
||||
lat, lon = (coords['lat'], coords['lon'])
|
||||
if (lat, lon) in latlon_list:
|
||||
receiver_ids.append(recid)
|
||||
return receiver_ids
|
||||
|
||||
|
||||
def coords_unique_receivers(receivers_dict):
|
||||
coords_unique = []
|
||||
for coords in receivers_dict.values():
|
||||
latlon_tuple = (coords['lat'], coords['lon'])
|
||||
if not latlon_tuple in coords_unique:
|
||||
coords_unique.append(latlon_tuple)
|
||||
return coords_unique
|
||||
|
||||
#def plot_residuals()
|
||||
|
||||
def main():
|
||||
#wdir = '/rscratch/minos13/marcel/fmtomo_alparray/alparray_mantle_diehl_crust_included_v3_hf'
|
||||
wdir = '/data/AlpArray_Data/fmtomo/v5/crust_incl_hf_sm_FIX_DTS_grad_sm30_dm10'
|
||||
os.chdir(wdir)
|
||||
|
||||
receivers_dict = organize_receivers('receivers.in')
|
||||
|
||||
print(wdir)
|
||||
misfits = {}
|
||||
mean_residuals = {}
|
||||
for extract_network in [None, 'Z3', 'ZS', ]:
|
||||
misfits[extract_network] = []
|
||||
mean_residuals[extract_network] = []
|
||||
with open('/rscratch/minos13/marcel/alparray/station_coords.json', 'r') as infile:
|
||||
station_coords = json.load(infile)
|
||||
|
||||
print('\nCalculating residuals for network:', extract_network)
|
||||
if extract_network:
|
||||
station_coords = extract_stations_for_network(station_coords, extract_network)
|
||||
latlon_dict = sort_stations_for_search(station_coords, receivers_dict)
|
||||
rec_ids = get_receiver_ids(list(latlon_dict.keys()), receivers_dict)
|
||||
|
||||
otimes = np.genfromtxt('otimes.dat', skip_header=1)
|
||||
#otimes_orig = np.genfromtxt('otimes_orig.dat', skip_header=1)
|
||||
ttimes = otimes[:, 4]
|
||||
uncs = otimes[:, 5]
|
||||
nsrc = int(otimes[-1, 1])
|
||||
|
||||
rtimes = np.genfromtxt('rtimes_tele.dat')[:, 4]
|
||||
|
||||
for itstep in range(0, 25):
|
||||
nRays = 0
|
||||
mtimes_all = np.genfromtxt('it_{}/arrivals.dat'.format(itstep))
|
||||
mtimes = mtimes_all[:, 4]
|
||||
mtimes_diff = mtimes - rtimes
|
||||
|
||||
for srcid in range(nsrc):
|
||||
srcid += 1
|
||||
# get all indices of current source id
|
||||
indices = np.where(mtimes_all[:, 1] == srcid)
|
||||
nRays += len(indices[0])
|
||||
mtimes_diff[indices] -= np.mean(mtimes_diff[indices])
|
||||
|
||||
# get all indices that are in rec_ids list as well
|
||||
mask_recs = np.isin(mtimes_all[:, 0].astype(int), rec_ids)
|
||||
|
||||
mf = calc_misfit(ttimes[mask_recs], mtimes_diff[mask_recs], uncs[mask_recs])
|
||||
sr = calc_mean_residuals(ttimes[mask_recs], mtimes_diff[mask_recs])
|
||||
misfits[extract_network].append(mf)
|
||||
mean_residuals[extract_network].append(sr)
|
||||
print('It: {}, misfit: {}, mean res: {}, nrays: {}'.format(itstep, mf, sr, nRays))
|
||||
|
||||
#plt.plot(mean_residuals[extract_network], label=extract_network)
|
||||
plt.plot(misfits[extract_network], label=extract_network)
|
||||
|
||||
plt.title(wdir)
|
||||
plt.ylabel('Mean residuals [s]')
|
||||
plt.xlabel('Iteration')
|
||||
plt.legend()
|
||||
plt.show()
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import glob, os, shutil
|
||||
|
||||
from fmtomo_tools.fmtomo_grid_utils import read_vgrid, write_vgrid
|
||||
|
||||
pjoin = os.path.join
|
||||
|
||||
pwd = '/data/AlpArray_Data/fmtomo/v6/crust_incl_hf_sm_FIX_DTS_grad_sm30_dm10_EASI_test_Plomerova/'
|
||||
|
||||
vgrid_file_in = pjoin(pwd, 'vgrids_dts_crust.in')
|
||||
vgrid_file_out = pjoin(pwd, 'vgrids_dts_crust_variance_slice.in')
|
||||
|
||||
latmin_rec, latmax_rec = 44.8, 51.3
|
||||
lonmin_rec, lonmax_rec = 11.94, 14.66
|
||||
|
||||
latmin_grid, latmax_grid = 44.45, 52.55
|
||||
lonmin_grid, lonmax_grid = 10.83, 15.77
|
||||
|
||||
|
||||
def modify_pickfiles():
|
||||
picks_path = pjoin(pwd, 'picks_orig')
|
||||
os.chdir(picks_path)
|
||||
|
||||
outdir = pjoin(pwd, 'picks')
|
||||
|
||||
|
||||
infiles = glob.glob('*.ttf')
|
||||
for infile in infiles:
|
||||
lines_out = []
|
||||
with open(infile, 'r') as fid:
|
||||
eventid = infile.split('.ttf')[0]
|
||||
npicks = int(fid.readline())
|
||||
|
||||
# copy source/phase header
|
||||
for _ in range(2):
|
||||
lines_out.append(fid.readline())
|
||||
|
||||
for line in fid:
|
||||
lat, lon = [float(item) for item in line.split()[:2]]
|
||||
if latmin_rec < lat < latmax_rec and lonmin_rec < lon < lonmax_rec:
|
||||
lines_out.append(line)
|
||||
|
||||
fn_out = pjoin(outdir, infile)
|
||||
with open(fn_out, 'w') as outfile:
|
||||
# number of picks: list contents - 2 header lines
|
||||
outfile.write(f'{len(lines_out) - 2}\n')
|
||||
for line in lines_out:
|
||||
outfile.write(line)
|
||||
|
||||
print(f'Modified {eventid}: Removed {npicks - len(lines_out)} out of {npicks} picks/stations')
|
||||
|
||||
os.chdir(pwd)
|
||||
|
||||
|
||||
def modify_variance_slice():
|
||||
grid, gridN, gridDelta, gridStart = read_vgrid(vgrid_file_in)
|
||||
|
||||
for index, latlon in enumerate(zip(grid['lats'], grid['lons'])):
|
||||
lat, lon = latlon
|
||||
if not latmin_grid < lat < latmax_grid or not lonmin_grid < lon < lonmax_grid:
|
||||
grid['covs'][index] = 1e-6
|
||||
|
||||
write_vgrid(grid, gridN, gridDelta, gridStart, vgrid_file_out)
|
||||
|
||||
|
||||
#modify_pickfiles()
|
||||
modify_variance_slice()
|
||||
@@ -1,294 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
import os
|
||||
|
||||
import numpy as np
|
||||
from scipy.interpolate import RegularGridInterpolator
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
from pylot.tomography.fmtomo_tools.fmtomo_teleseismic_utils import export_otimes, organize_receivers
|
||||
|
||||
def crustal_correction_using_differences(fname_otimes, fname_otimes_diff, fname_otimes_out):
|
||||
otimes = np.loadtxt(fname_otimes, skiprows=1)
|
||||
otimes_diff = np.loadtxt(fname_otimes_diff, skiprows=1)
|
||||
|
||||
src_means = source_means(otimes_diff)
|
||||
|
||||
#mean_diffs = np.mean([float(item.split()[4]) for item in otimes_diff[1:]])
|
||||
print('Mean_diffs of all sources: ', np.mean(list(src_means.values())))
|
||||
# TODO: Check if demeaning is useful for crustal correction using synthetics (was it b4 using ak135_diehl 1D?)
|
||||
# Update 27.5.2020: If not demeaned, overall velocity perturbation shifted to negative in whole upper mantle
|
||||
# BUT: demean has to be executed for each source, not with global mean!
|
||||
|
||||
with open(fname_otimes_out, 'w') as outfile:
|
||||
outfile.write('{}\n'.format(len(otimes)))
|
||||
for index, line in enumerate(otimes):
|
||||
src_id = line[1]
|
||||
ttime = line[4]
|
||||
ttime_diff = otimes_diff[index][4]
|
||||
new_time = ttime - ttime_diff + src_means[src_id]
|
||||
line[4] = new_time
|
||||
for col_index, item in enumerate(line):
|
||||
if col_index < 4:
|
||||
fmt = '{:10g} '
|
||||
else:
|
||||
fmt = '{:10.8f} '
|
||||
outfile.write(fmt.format(item))
|
||||
outfile.write('\n')
|
||||
|
||||
|
||||
def source_means(otimes):
|
||||
# catch all source ids from otimes file
|
||||
src_ids = np.unique(otimes[:, 1])
|
||||
print('Source IDs:', src_ids)
|
||||
|
||||
# create dictionary containing means of travel time (differences) for each source (key)
|
||||
src_means = {src_id: np.mean(otimes[otimes[:, 1] == src_id][:, 4]) for src_id in src_ids}
|
||||
|
||||
print('Source means:')
|
||||
for srcid, val in src_means.items():
|
||||
print(srcid, ': ', val)
|
||||
|
||||
return src_means
|
||||
|
||||
|
||||
def crustal_correction_using_residual_map(fname_otimes, fname_residuals, receivers_in, fname_otimes_out):
|
||||
''' correct using residuals from numpy file with an array of shape (lons, lats, residuals)'''
|
||||
otimes = np.loadtxt(fname_otimes, skiprows=1)
|
||||
rec_dict = organize_receivers(receivers_in)
|
||||
lons, lats, res = np.load(fname_residuals, allow_pickle=True)
|
||||
|
||||
lonsU = np.unique(lons)
|
||||
latsU = np.unique(lats)
|
||||
#grid = (lonsU, latsU)
|
||||
#shape = [len(item) for item in grid]
|
||||
#res = res.reshape(shape)
|
||||
|
||||
# not very efficient but very lazy (just interpolate value for each ray, i.e. receiver, not unique,
|
||||
# which is quite redundant)
|
||||
rginter = RegularGridInterpolator((lonsU, latsU), res, bounds_error=False, fill_value=0.)
|
||||
|
||||
test_rginter(rginter)
|
||||
|
||||
for line in otimes:
|
||||
rec_id = int(line[0])
|
||||
lonlat = (rec_dict[rec_id]['lon'], rec_dict[rec_id]['lat'])
|
||||
res = rginter(lonlat)
|
||||
line[4] -= res
|
||||
|
||||
src_means = source_means(otimes)
|
||||
|
||||
for src_id, src_mean in src_means.items():
|
||||
otimes[otimes[:, 1] == src_id, 4] -= src_mean
|
||||
|
||||
with open(fname_otimes_out, 'w') as outfile:
|
||||
outfile.write('{}\n'.format(len(otimes)))
|
||||
for line in otimes:
|
||||
for col_index, item in enumerate(line):
|
||||
if col_index < 4:
|
||||
fmt = '{:10g} '
|
||||
else:
|
||||
fmt = '{:10.8f} '
|
||||
outfile.write(fmt.format(item))
|
||||
outfile.write('\n')
|
||||
|
||||
|
||||
def test_rginter(rginter):
|
||||
lons = np.linspace(0, 22, 220)
|
||||
lats = np.linspace(40, 52, 120)
|
||||
lonsg, latsg = np.meshgrid(lons, lats)
|
||||
data = rginter((lonsg, latsg))
|
||||
pcm = plt.pcolormesh(lonsg, latsg, data)
|
||||
plt.colorbar(pcm)
|
||||
plt.show()
|
||||
|
||||
|
||||
def get_otimes_diff(fname_arrivals1, fname_arrivals2, fname_out='otimes_diff.dat'):
|
||||
'''
|
||||
Calculate file containing travel time differences between fname_arrivals1 and fname_arrivals2, e.g. for crustal
|
||||
correction.
|
||||
:param fname_arrivals1:
|
||||
:param fname_arrivals2:
|
||||
:return:
|
||||
'''
|
||||
with open(fname_arrivals1, 'r') as infile:
|
||||
arrivals1 = infile.readlines()
|
||||
with open(fname_arrivals2, 'r') as infile:
|
||||
arrivals2 = infile.readlines()
|
||||
|
||||
assert len(arrivals1) == len(arrivals2), 'Length of input arrival files differs'
|
||||
|
||||
print('Calculating differences between file {} and {}'.format(fname_arrivals1, fname_arrivals2))
|
||||
columnString = '{} '
|
||||
|
||||
nArrivals = len(arrivals1)
|
||||
with open(fname_out, 'w') as outfile:
|
||||
outfile.write('{}\n'.format(nArrivals))
|
||||
for index in range(nArrivals):
|
||||
line1 = arrivals1[index]
|
||||
line2 = arrivals2[index]
|
||||
for item in line1.split()[:4]:
|
||||
outfile.write(columnString.format(item))
|
||||
diff = float(line1.split()[4]) - float(line2.split()[4])
|
||||
outfile.write(columnString.format(diff))
|
||||
outfile.write('\n')
|
||||
print('Wrote {} lines to file {}'.format(nArrivals, fname_out))
|
||||
|
||||
|
||||
def get_synthetic_obsdata_legacy(fname_m1, fname_m2, fname_otimes_orig, fname_out='otimes_modif.dat'):#, p_err=0.1):
|
||||
'''
|
||||
Create synthetic obsdata of model 1 relative to model 2 (usually 1D model synthetic travel times)
|
||||
:param fname_m1: arrivals.dat file of synthetic travel times (e.g. from checkerboard test)
|
||||
:param fname_m2: arrivals.dat file of synthetic travel times (e.g. ak135 model)
|
||||
:param fname_otimes_orig: original otimes file for pick uncertainties
|
||||
:param fname_out: otimes.dat file with output
|
||||
:param p_err: picking error
|
||||
:return:
|
||||
'''
|
||||
with open(fname_m1, 'r') as infile:
|
||||
arrivals_model1 = infile.readlines()
|
||||
with open(fname_m2, 'r') as infile:
|
||||
arrivals_model2 = infile.readlines()
|
||||
with open(fname_otimes_orig, 'r') as infile:
|
||||
otimes = infile.readlines()[1:]
|
||||
|
||||
assert(len(arrivals_model1) == len(arrivals_model2) == len(otimes)), 'missmatch in lengths of files!'
|
||||
|
||||
diffs = []
|
||||
with open(fname_out, 'w') as outfile:
|
||||
outfile.write('{}\n'.format(len(arrivals_model1)))
|
||||
|
||||
for line_model1, line_model2, line_otimes in zip(arrivals_model1, arrivals_model2, otimes):
|
||||
for item in line_model2.split()[:4]:
|
||||
outfile.write('{} '.format(item))
|
||||
|
||||
ttime_model1 = float(line_model1.split()[4])
|
||||
ttime_model2 = float(line_model2.split()[4])
|
||||
pickerror = float(line_otimes.split()[5])
|
||||
# pickerror = (p_err+2*p_err*abs(np.random.randn()))
|
||||
|
||||
ttime_diff = ttime_model1 - ttime_model2
|
||||
diffs.append(ttime_diff)
|
||||
|
||||
outfile.write('{} {}\n'.format(ttime_diff, pickerror))
|
||||
|
||||
mean_diff = np.mean(diffs)
|
||||
abs_mean_diff = np.mean(np.abs(diffs))
|
||||
|
||||
print('Mean_diff: {} - abs_mean_diff: {}'.format(mean_diff, abs_mean_diff))
|
||||
print('Done with {} travel times. Output in file: {}'.format(len(arrivals_model1), fname_out))
|
||||
|
||||
|
||||
def get_synthetic_obsdata(fname_m1, fname_m2, fname_otimes_orig, fname_out='otimes_modif.dat', sigma='original'):
|
||||
'''
|
||||
Create synthetic obsdata of model 1 relative to model 2 (usually 1D model synthetic travel times)
|
||||
:param fname_m1: arrivals.dat file of synthetic travel times (e.g. from checkerboard test)
|
||||
:param fname_m2: arrivals.dat file of synthetic travel times (e.g. ak135 model)
|
||||
:param fname_otimes_orig: original otimes file for pick uncertainties
|
||||
:param fname_out: otimes.dat file with output
|
||||
:param p_err: picking error
|
||||
:return:
|
||||
'''
|
||||
arrivals_model1 = np.genfromtxt(fname_m1)
|
||||
arrivals_model2 = np.genfromtxt(fname_m2)
|
||||
otimes = np.genfromtxt(fname_otimes_orig, skip_header=1)
|
||||
|
||||
assert(len(arrivals_model1) == len(arrivals_model2) == len(otimes)), 'missmatch in lengths of files!'
|
||||
|
||||
# get new array as copy from model1 after deleting last column (not needed for otimes)
|
||||
arrivals_diff = np.delete(arrivals_model1, 6, 1)
|
||||
|
||||
# overwrite time column by diffs
|
||||
arrivals_diff[:, 4] -= arrivals_model2[:, 4]
|
||||
# overwrite last column by pick errors
|
||||
arrivals_diff[:, 5] = otimes[:, 5]
|
||||
|
||||
# get max nSrc (in last line of sorted arrivals files)
|
||||
nSrc = int(arrivals_diff[-1, 1])
|
||||
|
||||
print('N sources:', nSrc)
|
||||
if sigma not in [None, False]:
|
||||
print('Applying gaussian noise with sigma={}'.format(sigma))
|
||||
if sigma == 'original':
|
||||
sigma_val = otimes[:, 5]
|
||||
else:
|
||||
sigma_val = sigma
|
||||
gauss_noise = np.random.normal(0., sigma_val, len(arrivals_diff[:, 4]))
|
||||
plt.axhline(0., color='grey')
|
||||
plt.vlines(np.arange(0, len(arrivals_diff)), arrivals_diff[:, 4], arrivals_diff[:, 4] + gauss_noise,
|
||||
color='grey', linestyles='dashed')
|
||||
plt.plot(arrivals_diff[:, 4], 'r.')
|
||||
arrivals_diff[:, 4] += gauss_noise
|
||||
|
||||
for index in range(nSrc):
|
||||
src_id = index + 1
|
||||
# get indices for this source ID
|
||||
indices = np.where(arrivals_diff[:, 1] == src_id)
|
||||
# get mean for that srcid
|
||||
mean = np.mean(arrivals_diff[:, 4][indices])
|
||||
print('Srcid: {}, mean: {}'.format(src_id, mean))
|
||||
# de-mean array
|
||||
arrivals_diff[:, 4][indices] -= mean
|
||||
|
||||
if sigma not in [None, False]:
|
||||
plt.axhline(0., color='grey')
|
||||
#plt.vlines(np.arange(0, len(arrivals_diff)), arrivals_diff[:, 4], arrivals_diff[:, 4] + gauss_noise)
|
||||
plt.plot(arrivals_diff[:, 4], 'b.')
|
||||
plt.figure()
|
||||
plt.hist(gauss_noise, bins=100)
|
||||
plt.show()
|
||||
|
||||
export_otimes(arrivals_diff, fname_out)
|
||||
|
||||
|
||||
def plot_histograms_for_crustal_corrections():
|
||||
import matplotlib.pyplot as plt
|
||||
ttdiffs_no_crust = []
|
||||
ttdiffs_crust = []
|
||||
ttdiffs_corrected = []
|
||||
|
||||
#get_synthetic_obsdata('it_synth_forward/arrivals.dat', 'arrivals_ak135_diehl.dat', 'otimes_three_boxes_no_crust.dat')
|
||||
#get_synthetic_obsdata('it_synth_forward_with_crust/arrivals.dat', 'arrivals_ak135_diehl.dat', 'otimes_three_boxes_with_diehl_crust.dat')
|
||||
with open('otimes_three_boxes_with_diehl_crust.dat', 'r') as infile:
|
||||
lines_crust = infile.readlines()[1:]
|
||||
with open('otimes_three_boxes_no_crust.dat', 'r') as infile:
|
||||
lines_no_crust = infile.readlines()[1:]
|
||||
with open('otimes_three_boxes_crustal_corrected.dat', 'r') as infile:
|
||||
lines_corrected = infile.readlines()[1:]
|
||||
|
||||
for line in lines_crust:
|
||||
ttdiffs_crust.append(float(line.split()[4]))
|
||||
for line in lines_no_crust:
|
||||
ttdiffs_no_crust.append(float(line.split()[4]))
|
||||
for line in lines_corrected:
|
||||
ttdiffs_corrected.append(float(line.split()[4]))
|
||||
|
||||
plt.hist(ttdiffs_crust, bins=100, label='tt-diff including crustal structure.')
|
||||
plt.hist(ttdiffs_no_crust, bins=100, label='tt-diff without crustal structure')
|
||||
plt.hist(ttdiffs_corrected, bins=100, label='tt-diff after correcting for crustal structure')
|
||||
plt.xlabel('Travel time differences to ak135_diehl 1D model [s]')
|
||||
plt.legend()
|
||||
|
||||
if __name__ == '__main__':
|
||||
#wdir = '/rscratch/minos13/marcel/fmtomo_alparray/alparray_mantle_diehl_crustal_corrections_v3_hf'
|
||||
#fname1 = os.path.join(wdir, 'arrivals_ak135di_crust.dat')
|
||||
#fname2 = os.path.join(wdir, 'arrivals_ak135di.dat')
|
||||
#get_otimes_diff(fname1, fname2, os.path.join(wdir, 'otimes_diff.dat'))
|
||||
|
||||
#get_synthetic_obsdata('arrivals_cb_N2.dat', 'arrivals_ak135.dat', 'otimes_cb_N2.dat')
|
||||
#os.chdir('/data/AlpArray_Data/fmtomo/v4/different_test_runs/alparray_mantle_waldhauser_crust_corrected_covs_hf_gradient_smoothing')
|
||||
#os.chdir('/data/AlpArray_Data/fmtomo/v4/different_test_runs/alparray_mantle_waldhauser_crust_corrected_covs_hf_gradient_smoothing')
|
||||
#crustal_correction_using_residual_map('otimes.dat', 'wh_residuals.npy', 'receivers.in', 'otimes_corr_wh.dat')
|
||||
|
||||
#os.chdir('/data/AlpArray_Data/fmtomo/v4/different_test_runs/alparray_mantle_diehl_crust_corrected_residuals_stacked_gradient_smoothing')
|
||||
#crustal_correction_using_residual_map('otimes.dat', 'diehl_2009_residuals.npy', 'receivers.in', 'otimes_corr_diehl.dat')
|
||||
|
||||
#os.chdir('/data/AlpArray_Data/fmtomo/v6/crust_corrected_VERTICAL_hf_sm_FIX_DTS_grad_sm30_dm10')
|
||||
#crustal_correction_using_residual_map('otimes.dat', 'dts_filt_12.5_12.5_7.5_CRUST_ONLY_RESIDUALS.npy', 'receivers.in', 'otimes_corr_dts.dat')
|
||||
|
||||
os.chdir('/data/AlpArray_Data/fmtomo/v6/crust_corrected_WH_hf_sm_FIX_DTS_grad_sm30_dm10')
|
||||
crustal_correction_using_residual_map('otimes.dat', 'wh_residuals_SORTED.npy', 'receivers.in', 'otimes_corr_wh.dat')
|
||||
|
||||
|
||||
|
||||
#crustal_correction_using_residual_map('otimes.dat', 'wh_residuals.npy', 'receivers.in', 'otimes_corr_wh.dat')
|
||||
@@ -1,154 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Different functions to modify FMTOMO velocity(inversion) grid
|
||||
import argparse
|
||||
import numpy as np
|
||||
from math import erf
|
||||
|
||||
from pylot.tomography.fmtomo_tools.fmtomo_grid_utils import read_vgrid, write_vgrid, write_vtk
|
||||
|
||||
|
||||
def main(fname_in, fname_out, fname_vtk_out=None, cov_perc=0.15, bounds=[40, 60]):
|
||||
vgrid, gridN, gridDelta, gridStart = read_vgrid(fname_in)
|
||||
|
||||
print('Modifying covariances...')
|
||||
vgrid['covs'] = []
|
||||
for depth, vp in zip(vgrid['depths'], vgrid['vps']):
|
||||
vgrid['covs'].append(covariance_for_depth(depth, vp, cov_perc, bounds))
|
||||
|
||||
write_vgrid(vgrid, npts=gridN, delta=gridDelta, start=gridStart, fname=fname_out)
|
||||
if fname_vtk_out:
|
||||
write_vtk(vgrid, fname_vtk_out, write_data=['vps', 'covs'])
|
||||
|
||||
|
||||
def modify_vgrid_box(vgrid, vgrid_key, min_lon, max_lon, min_lat, max_lat, min_depth, max_depth,
|
||||
val_center, val_border, scale_factor, use_erf=True, extend_to_top=False):
|
||||
'''
|
||||
Modify a value (vgrid_key) of vgrids.in using an error function. Value will be increase smoothly to the borders
|
||||
of the external model. This function was used to prevent changes of the inversion result in the region of the
|
||||
initial crustal model that was corrected for. Value smoothly increases at the borders of the initial model.
|
||||
'''
|
||||
|
||||
# get central points of external model (here value will be minimal)
|
||||
c_lon = 0.5 * (min_lon + max_lon)
|
||||
c_lat = 0.5 * (min_lat + max_lat)
|
||||
c_depth = 0.5 * (min_depth + max_depth)
|
||||
|
||||
# get half the extent of the model dimensions for function scaling
|
||||
dlon = 0.5 * (max_lon - min_lon)
|
||||
dlat = 0.5 * (max_lat - min_lat)
|
||||
ddepth = 0.5 * (max_depth - min_depth)
|
||||
|
||||
print('Modifying {}...'.format(vgrid_key))
|
||||
print('Center {key}: {val_c}, border {key}: {val_b}'.format(key=vgrid_key, val_c=val_center, val_b=val_border))
|
||||
if not vgrid_key in vgrid.keys() or not vgrid[vgrid_key]:
|
||||
vgrid[vgrid_key] = list(np.ones(len(vgrid['depths'])))
|
||||
for index, tup in enumerate(zip(vgrid['lons'], vgrid['lats'], vgrid['depths'])):
|
||||
lon, lat, depth = tup
|
||||
if (min_lon <= lon <= max_lon and min_lat <= lat <= max_lat and depth <= max_depth):
|
||||
x = abs(lon - c_lon) / dlon
|
||||
y = abs(lat - c_lat) / dlat
|
||||
if extend_to_top and depth < c_depth:
|
||||
z = 0
|
||||
else:
|
||||
z = abs(depth - c_depth) / ddepth
|
||||
if use_erf:
|
||||
point_val = smoothing_erf(x, y, z, val_center, val_border, scale_factor=scale_factor)
|
||||
else:
|
||||
point_val = val_center
|
||||
else:
|
||||
point_val = val_border
|
||||
vgrid[vgrid_key][index] *= point_val
|
||||
|
||||
return vgrid
|
||||
|
||||
|
||||
def modify_vgrid_gradient(vgrid, vgrid_key, val_top, val_bot):
|
||||
'''
|
||||
Modify a value (vgrid_key) of vgrids.in using a linear gradient from val_top to val_bot
|
||||
'''
|
||||
|
||||
print('Modifying {} with linear gradient...'.format(vgrid_key))
|
||||
print('Top: {}, bot: {}'.format(val_top, val_bot))
|
||||
if not vgrid_key in vgrid.keys():
|
||||
vgrid[vgrid_key] = list(np.ones(len(vgrid['depths'])))
|
||||
depth_min = min(vgrid['depths'])
|
||||
depth_max = max(vgrid['depths'])
|
||||
for index, tup in enumerate(zip(vgrid['lons'], vgrid['lats'], vgrid['depths'])):
|
||||
lon, lat, depth = tup
|
||||
vgrid[vgrid_key][index] *= linear_gradient(depth, depth_min=depth_min, depth_max=depth_max, val_top=val_top,
|
||||
val_bot=val_bot)
|
||||
return vgrid
|
||||
|
||||
|
||||
def linear_gradient(depth, depth_min, depth_max, val_top, val_bot):
|
||||
return (val_bot - val_top) * depth / (depth_max - depth_min) + val_top
|
||||
|
||||
|
||||
def smoothing_erf(x, y, z, val_border, val_center, scale_factor=1.):
|
||||
a = val_center
|
||||
b = val_border
|
||||
sx = sy = sz = scale_factor
|
||||
f = (a - b) * (1. / 3. * (erf(2. * sx * (x - 1)) + erf(2. * sy * (y - 1)) + erf(2. * sz * (z - 1))) + 1.) + b
|
||||
return f
|
||||
|
||||
|
||||
def covariance_for_depth(depth, vp, cov_perc, bounds):
|
||||
'''
|
||||
Function that returns covariance values for certain depths, written to give low variance to crust, intermediate
|
||||
to an interlayer and high variance to everything else.
|
||||
:param depth: depth in kilometers
|
||||
:return: covariance
|
||||
'''
|
||||
if depth <= bounds[0]:
|
||||
return 0.05
|
||||
elif bounds[0] < depth <= bounds[1]:
|
||||
return 0.5 * cov_perc * vp
|
||||
else:
|
||||
return cov_perc * vp
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description='Prepare grid for fm3d teleseismic hybrid calculation.')
|
||||
parser.add_argument('fname_in', help='input filename (vgrids.in)')
|
||||
parser.add_argument('fname_out', help='output filename (vgrids_new.in)')
|
||||
parser.add_argument('--fname_out_vtk', default=None, help='vtk output filename')
|
||||
parser.add_argument('--cov_border', default=1.0, help='covariance for the model outside crustal model boundaries')
|
||||
parser.add_argument('--cov_center', default=0.05, help='maximum covariance in the center of the crustal model')
|
||||
parser.add_argument('--smooth_border', default=1., help='smoothing factor for the rest of the box')
|
||||
parser.add_argument('--smooth_center', default=0.5, help='smoothing factor inside SWATH-D box')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
vgrid, gridN, gridDelta, gridStart = read_vgrid(args.fname_in)
|
||||
|
||||
#main(args.fname_in, args.fname_out, args.fname_out_vtk)
|
||||
|
||||
#vgrid = modify_vgrid_gradient(vgrid, 'smoothfactors', 1.0, 2.0)
|
||||
|
||||
# bounds for waldhauser residual corrections
|
||||
wh_bounds = dict(lon0 = 3.23, lon1 = 13.96, lat0 = 43.18, lat1 = 49.45)
|
||||
vgrid = modify_vgrid_box(vgrid, vgrid_key='covs', min_lon=wh_bounds['lon0'], max_lon=wh_bounds['lon1'],
|
||||
min_lat=wh_bounds['lat0'], max_lat=wh_bounds['lat1'], min_depth=-5.0, max_depth=60.0,
|
||||
val_center=float(args.cov_center), val_border=float(args.cov_border),
|
||||
scale_factor=3, use_erf=True, extend_to_top=True)
|
||||
|
||||
#vgrid = modify_vgrid_box(vgrid, vgrid_key='covs', min_lon=2.2547, max_lon=17.0999, min_lat=41.0517,
|
||||
# max_lat=50.4984, min_depth=-5.0, max_depth=70.0, val_center=float(args.cov_center),
|
||||
# val_border=float(args.cov_border), scale_factor=3, use_erf=True, extend_to_top=True)
|
||||
|
||||
#vgrid = modify_vgrid_box(vgrid, vgrid_key='smoothfactors', min_lon=8.9, max_lon=15.3, min_lat=45.,
|
||||
# max_lat=48., min_depth=-15.0, max_depth=610.0, val_center=float(args.smooth_center),
|
||||
# val_border=float(args.smooth_border), scale_factor=1., use_erf=True)
|
||||
|
||||
# MP MP TEST TEST TEST ++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
#grid = modify_vgrid_box(grid, vgrid_key='smoothfactors', min_lon=11., max_lon=35., min_lat=30.,
|
||||
# max_lat=61., min_depth=-5.0, max_depth=600.0, val_center=float(args.smooth_center),
|
||||
# val_border=float(args.smooth_border), scale_factor=.4)
|
||||
# MP MP TEST TEST TEST ------------------------------------------
|
||||
|
||||
write_vgrid(vgrid, npts=gridN, delta=gridDelta, start=gridStart, fname=args.fname_out)
|
||||
if args.fname_out_vtk:
|
||||
write_vtk(vgrid, args.fname_out_vtk, write_data=['vps', 'covs', 'smoothfactors'])
|
||||
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import argparse
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
def plot_otimes(otimes_fname):
|
||||
with open(otimes_fname, 'r') as infile:
|
||||
input_list = infile.readlines()[1:]
|
||||
ray_id = [int(line.split()[0]) for line in input_list]
|
||||
ttimes = [float(line.split()[-2]) for line in input_list]
|
||||
uncertainties = [float(line.split()[-1]) for line in input_list]
|
||||
plt.axhline(0, linestyle=':', color='k')
|
||||
plt.errorbar(ray_id, ttimes, yerr=uncertainties, fmt='o', markersize=1, ecolor='0.5', elinewidth=0.5)
|
||||
plt.show()
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description='Plot ttimes with errors of FMTOMO file otimes.dat')
|
||||
parser.add_argument('infile', help='inputfile')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
plot_otimes(args.infile)
|
||||
@@ -1,341 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import os
|
||||
import argparse
|
||||
import json
|
||||
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
from scipy.interpolate import griddata
|
||||
|
||||
from obspy.geodetics.base import gps2dist_azimuth
|
||||
|
||||
import cartopy.crs as ccrs
|
||||
|
||||
from pylot.tomography.fmtomo_tools.compare_arrivals_hybrid import organize_receivers, organize_sources, organize_event_names
|
||||
from pylot.tomography.map_utils import make_map
|
||||
from pylot.tomography.utils import normed_figure
|
||||
|
||||
|
||||
def plot_map(otimes_fname, sources_fname, receivers_fname, isf, rtimes_fname=None, arrivals_fname=None, colorbar=True,
|
||||
title=True, stack=False, source_ids=(), savefig_dir='', demean=True, absolute=False, clat=42.25, clon=17.5,#clat=46., clon=11.,
|
||||
file_ext='png', max_abs=3., relative_to_otimes=False, only_otimes=False):
|
||||
if savefig_dir:
|
||||
if not os.path.isdir(savefig_dir):
|
||||
os.mkdir(savefig_dir)
|
||||
|
||||
src_dict = organize_sources(sources_fname)
|
||||
rec_dict = organize_receivers(receivers_fname)
|
||||
eventnames = organize_event_names(isf)
|
||||
|
||||
residuals_dict = read_fmtomo_tt_file(otimes_fname, rec_dict, rtimes_fname=rtimes_fname, synth_fname=arrivals_fname,
|
||||
demean=demean, absolute=absolute, height_correction_vp=5.5,
|
||||
relative_to_otimes=relative_to_otimes, only_otimes=only_otimes)
|
||||
|
||||
if stack:
|
||||
residuals_dict = stack_sources(residuals_dict)
|
||||
with open('stacked_residuals.json', 'w') as outfile:
|
||||
json.dump(residuals_dict, outfile)
|
||||
|
||||
count = 0
|
||||
if savefig_dir:
|
||||
fig = normed_figure(width_cm=12, ratio=12./9.)
|
||||
#fig = plt.figure(figsize=(12, 9))
|
||||
else:
|
||||
fig = plt.figure()
|
||||
if stack or source_ids is not [] or savefig_dir:
|
||||
count = 1
|
||||
|
||||
# increase point size if not stacked
|
||||
sizefactor_stacked = {True: 30,
|
||||
False: .2e4}
|
||||
|
||||
for src_id, dic in residuals_dict.items():
|
||||
eventname = eventnames[src_id - 1]
|
||||
if source_ids != []:
|
||||
if not src_id in source_ids:
|
||||
continue
|
||||
if max_abs == 'auto':
|
||||
max_abs = np.max(np.abs(np.array(dic['ttimes'])))
|
||||
# if not stack and not savefig_dir and source_ids == []:
|
||||
# count += 1
|
||||
# if count == 1:
|
||||
# ax = fig.add_subplot(3, 3, count)
|
||||
# ax0 = ax
|
||||
# else:
|
||||
# ax = fig.add_subplot(3, 3, count, sharex=ax0, sharey=ax0)
|
||||
#
|
||||
# ax.text(0.1, 0.9, 'Source ID {}, {}'.format(src_id, src_dict[src_id]['phase']), transform=ax.transAxes)
|
||||
# else:
|
||||
# ax = fig.add_subplot(111)
|
||||
if stack and title:
|
||||
plt.title('Stacked {} sources. Size relates to number of stacks per receiver.'.format(len(src_dict)),
|
||||
y=1.05)
|
||||
ax = make_map(draw_model=False, draw_faults=True, model_legends=False, clon=clon, clat=clat,
|
||||
width = 30, height = 21,) # , width=6e6, height=5e6)
|
||||
if not stack:
|
||||
slat, slon = src_dict[src_id]['lat'], src_dict[src_id]['lon']
|
||||
dist = plot_source(slat, slon)
|
||||
baz = gps2dist_azimuth(clat, clon, slat, slon, a=6.371e6, f=0)[1]
|
||||
if title:
|
||||
plt.title(
|
||||
'Plot of source {}. Distance: {:.0f}$^\circ$. BAZ: {:.0f}$^\circ$'.format(eventname, dist, baz),
|
||||
y=1.05)
|
||||
|
||||
#ax.set_xlabel('Longitude [$^\circ$]')
|
||||
#ax.set_ylabel('Latitude [$^\circ$]')
|
||||
|
||||
# prepare grids for contour plot
|
||||
#lonaxis = np.linspace(min(dic['lons']), max(dic['lons']), 250)
|
||||
#lataxis = np.linspace(min(dic['lats']), max(dic['lats']), 250)
|
||||
#longrid, latgrid = np.meshgrid(lonaxis, lataxis)
|
||||
#ttimes_grid = griddata((dic['lats'], dic['lons']), dic['ttimes'], (latgrid, longrid), method='linear')
|
||||
#levels = np.linspace(min(dic['ttimes']), max(dic['ttimes']), 75)
|
||||
|
||||
cmap = plt.get_cmap('seismic') if not absolute else plt.get_cmap('viridis')
|
||||
|
||||
vmin = -max_abs if not absolute else None
|
||||
vmax = max_abs if not absolute else None
|
||||
sc = ax.scatter(dic['lons'], dic['lats'], c=np.array(dic['ttimes']), cmap=cmap,
|
||||
vmin=vmin, vmax=vmax, edgecolors='grey', linewidths=0.2,
|
||||
s=sizefactor_stacked[stack]*np.array(dic['nttimes'])/len(src_dict), zorder=10,
|
||||
transform=ccrs.PlateCarree(), alpha=0.75)
|
||||
sc.set_edgecolor('0.')
|
||||
if colorbar:
|
||||
cb = plt.colorbar(sc, label='ttime [s]', shrink=0.5)
|
||||
|
||||
if stack:
|
||||
savefig_path = os.path.join(savefig_dir, 'stacked_events.{}'.format(file_ext))
|
||||
else:
|
||||
#fname = f'baz{baz:03.0f}_dist{dist:03.0f}_{eventname}_srcID{src_id}.{file_ext}'
|
||||
fname = f'{eventname}_srcID{src_id}.{file_ext}'
|
||||
savefig_path = os.path.join(savefig_dir, fname)
|
||||
|
||||
|
||||
if not savefig_dir and not count % 9:
|
||||
plt.show()
|
||||
fig = plt.figure()#figsize=(16, 9), dpi=300.)
|
||||
count = 0
|
||||
if savefig_dir:
|
||||
ax.figure.savefig(savefig_path, dpi=300., bbox_inches='tight', pad_inches=0.)
|
||||
print('Wrote file {}'.format(savefig_path))
|
||||
plt.clf()
|
||||
|
||||
if not savefig_dir:
|
||||
plt.show()
|
||||
|
||||
|
||||
def plot_source(lat, lon, clat=46., clon=11.):
|
||||
#basemap.drawgreatcircle(lon, lat, clon, clat, color='k', zorder=15, linestyle='dashed')
|
||||
dist, azim, bazim = gps2dist_azimuth(lat, lon, clat, clon, a=6.371e6, f=0)
|
||||
|
||||
dist_deg = dist/1000./np.pi/2./6371.*360.
|
||||
return dist_deg
|
||||
|
||||
#x = np.cos(np.deg2rad(azim))
|
||||
#y = np.sin(np.deg2rad(azim))
|
||||
#print(x, y)
|
||||
#ax.plot([0, x], [0, y], 'r', zorder=15)
|
||||
|
||||
|
||||
def read_fmtomo_tt_file(otimes_fname, rec_dict, synth_fname=None, rtimes_fname=None, demean=True, absolute=False,
|
||||
height_correction_vp=None, R=6371., relative_to_otimes=True, only_otimes=False):
|
||||
#with open(otimes_fname, 'r') as infile:
|
||||
# # skip first line for otimes.dat. Frist line contains N_rays
|
||||
# obsarray = infile.readlines()[1:]
|
||||
obsarray = np.loadtxt(otimes_fname, skiprows=1)
|
||||
|
||||
if height_correction_vp:
|
||||
if absolute:
|
||||
print('APPLICATION OF HEIGHT CORRECTION FOR ABS NOT IMPLEMENTED YET. Not needed!?')
|
||||
#print('Applying height correction of {} km/s'.format(height_correction_vp))
|
||||
else:
|
||||
height_correction_vp = None
|
||||
print('Will not apply height correction for relative values.')
|
||||
|
||||
if synth_fname:
|
||||
synth_array = np.genfromtxt(synth_fname)
|
||||
|
||||
if rtimes_fname:
|
||||
ref_array = np.genfromtxt(rtimes_fname)
|
||||
|
||||
if synth_fname and rtimes_fname:
|
||||
#with open(synth_fname, 'r') as infile:
|
||||
# synth_array = infile.readlines()
|
||||
#with open(rtimes_fname, 'r') as infile:
|
||||
# ref_array = infile.readlines()
|
||||
|
||||
#mean_obs = np.mean(obsarray[:, 4])
|
||||
#mean_synth = np.mean(synth_array[:, 4])
|
||||
#mean_ref = np.mean(ref_array[:, 4])
|
||||
|
||||
#mean_rel_synth = mean_synth - mean_ref
|
||||
#print('Means:\nobserved: {}, relative {}'.format(mean_obs, mean_rel_synth))
|
||||
synth_src_means = {}
|
||||
|
||||
nsrc = obsarray[-1, 1]
|
||||
for index in range(int(nsrc)):
|
||||
srcid = index + 1
|
||||
indices = np.where(synth_array[:, 1].astype(int) == srcid)
|
||||
synth_src_means[srcid] = np.mean(synth_array[indices, 4] - ref_array[indices, 4])
|
||||
|
||||
print('Average mean for sources: {} s'.format(np.mean(list(synth_src_means.values()))))
|
||||
|
||||
if demean:
|
||||
print('Removing mean from synthetic times...')
|
||||
else:
|
||||
print('Will not mean-correct travel times')
|
||||
# mean_rel_synth = 0.
|
||||
|
||||
residuals_dict = {}
|
||||
|
||||
for index, line in enumerate(obsarray):
|
||||
rec_id = int(line[0])
|
||||
src_id = int(line[1])
|
||||
ttime = line[4] if relative_to_otimes or only_otimes else 0
|
||||
|
||||
# get residuals from reference file if fname is given
|
||||
if synth_fname:
|
||||
# get synthetic time
|
||||
ttime_syn = synth_array[index][4]
|
||||
if rtimes_fname:
|
||||
# get reference time (1d travel time)
|
||||
ttime_ref = ref_array[index][4]
|
||||
if synth_fname and rtimes_fname:
|
||||
# calculate synthetic time relative to 1d travel time
|
||||
ttime_rel_syn = ttime_syn - ttime_ref
|
||||
#print(ttime_ref, ttime_syn, ttime_rel_syn, ttime, ttime-ttime_rel_syn)
|
||||
# calculate difference between relative observed and model times, multiply with -1 to get tsynth - tobs
|
||||
mean_rel_src = synth_src_means[srcid] if demean else 0.
|
||||
if not only_otimes:
|
||||
ttime = -1 * (ttime_rel_syn - ttime - mean_rel_src)
|
||||
if absolute:
|
||||
ttime = ttime_syn
|
||||
try:
|
||||
uncertainty = line[5]
|
||||
except:
|
||||
uncertainty = 0
|
||||
|
||||
# create dictionary for source if not exists
|
||||
if not src_id in residuals_dict.keys():
|
||||
residuals_dict[src_id] = {'lats': [],
|
||||
'lons': [],
|
||||
'rads': [],
|
||||
'ttimes':[],
|
||||
'nttimes': [],
|
||||
'uncerts': []}
|
||||
# chose correct dictionary
|
||||
dic = residuals_dict[src_id]
|
||||
# get coordinates from organized receivers dictionary
|
||||
dic['lats'].append(rec_dict[rec_id]['lat'])
|
||||
dic['lons'].append(rec_dict[rec_id]['lon'])
|
||||
dic['rads'].append(rec_dict[rec_id]['rad'])
|
||||
# rad is depth?
|
||||
#elev = - rec_dict[rec_id]['rad']
|
||||
#height_correction_vp = 0
|
||||
#station_height_corr = elev / (height_correction_vp) if height_correction_vp else 0.
|
||||
#if elev < 0: print(elev, station_height_corr)
|
||||
# MP MP NO HEIGHT CORRECTION FOR FMTOMO! REFTIMES CONTAIN STATION HIGHT!!!!!
|
||||
dic['ttimes'].append(ttime) # - station_height_corr)
|
||||
# number of ttimes will be set to 1 (determines size in scatter, only relevant for stacked ttimes)
|
||||
dic['nttimes'].append(1)
|
||||
dic['uncerts'].append(uncertainty)
|
||||
|
||||
return residuals_dict
|
||||
|
||||
|
||||
def stack_sources(residuals_dict):
|
||||
all_ttimes = {}
|
||||
all_uncs = {}
|
||||
for src_id, dic in residuals_dict.items():
|
||||
for index in range(len(dic['lats'])):
|
||||
lat = dic['lats'][index]
|
||||
lon = dic['lons'][index]
|
||||
rad = dic['rads'][index]
|
||||
source_tuple = (lat, lon, rad)
|
||||
if not source_tuple in all_ttimes.keys():
|
||||
all_ttimes[source_tuple] = []
|
||||
all_uncs[source_tuple] = []
|
||||
all_ttimes[source_tuple].append(dic['ttimes'][index])
|
||||
all_uncs[source_tuple].append(dic['uncerts'][index])
|
||||
# create new dictionary in the shape of residuals dict with only one source
|
||||
stacked_dict = {}
|
||||
stacked_dict[1] = {'lats': [],
|
||||
'lons': [],
|
||||
'rads': [],
|
||||
'ttimes':[],
|
||||
'nttimes': [],
|
||||
'misfits': []}
|
||||
|
||||
|
||||
for source_tuple in all_ttimes.keys():
|
||||
ttimes_list = all_ttimes[source_tuple]
|
||||
uncs_list = all_uncs[source_tuple]
|
||||
misfit = np.sum((np.array(ttimes_list) / np.array(uncs_list))**2) / len(ttimes_list)
|
||||
lat, lon, rad = source_tuple
|
||||
stacked_dict[1]['lats'].append(lat)
|
||||
stacked_dict[1]['lons'].append(lon)
|
||||
stacked_dict[1]['rads'].append(rad)
|
||||
stacked_dict[1]['ttimes'].append(np.sum(np.mean(ttimes_list)))
|
||||
stacked_dict[1]['nttimes'].append(len(ttimes_list))
|
||||
stacked_dict[1]['misfits'].append(misfit)
|
||||
#stacked_dict[1]['ttimes_list'].append(ttimes_list)
|
||||
|
||||
ttimes = [d['ttimes'] for d in stacked_dict.values()]
|
||||
plt.hist(ttimes, 100)
|
||||
plt.axvline(np.mean(ttimes), c='r')
|
||||
plt.xlabel('ttime [s]')
|
||||
|
||||
return stacked_dict
|
||||
|
||||
|
||||
def compare_residuals():
|
||||
wdir = '/rscratch/minos13/marcel/fmtomo_alparray/'
|
||||
os.chdir(wdir)
|
||||
with open('alparray_mantle_diehl_crust_included_v3_hf/stacked_residuals.json', 'r') as infile:
|
||||
sr = json.load(infile)
|
||||
with open('alparray_mantle_diehl_crust_included_v3_no_density_hf/stacked_residuals.json', 'r') as infile:
|
||||
srnd = json.load(infile)
|
||||
mfdiff = np.array(sr['1']['misfits']) - np.array(srnd['1']['misfits'])
|
||||
np.mean(mfdiff)
|
||||
sc = plt.scatter(sr['1']['lons'], sr['1']['lats'], c=mfdiff, cmap='seismic', vmin=-7.5, vmax=7.5)
|
||||
cb = plt.colorbar(sc)
|
||||
title = 'Difference in station misfit after 12 iterations (density kernel - no density).' \
|
||||
' Blue: decrease in residuals using density. Mean: {:.4f}'
|
||||
plt.title(title.format(np.mean(mfdiff)))
|
||||
plt.xlabel('Latitude (deg)')
|
||||
plt.ylabel('Longitude (deg)')
|
||||
cb.set_label('Misfit')
|
||||
sc.set_linewidth(0.2)
|
||||
sc.set_edgecolor('k')
|
||||
plt.show()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description='Plot residuals of fm3d output file (arrivals.dat format) on map.')
|
||||
parser.add_argument('--otimes', default='otimes.dat', help='otimes.dat file (default: otimes.dat)')
|
||||
parser.add_argument('--sources', default='sources.in', help='sources.in file (default: sources.in)')
|
||||
parser.add_argument('--receivers', default='receivers.in', help='receivers.in file (default receivers.in)')
|
||||
parser.add_argument('--sourcefile', default='input_source_file.in', help='input_source_file (default: input_source_file.in)')
|
||||
parser.add_argument('--arrivals', default=None, help='arrivals_file')
|
||||
parser.add_argument('--rtimes', default=None, help='reference_file')
|
||||
parser.add_argument('--fext', default='png', help='file extension for images (if -w)')
|
||||
parser.add_argument('-s', '--stack', action='store_true', dest='stack', help='stack picks')
|
||||
parser.add_argument('-w', '--write', action='store_true', default=False, help='write figures to disk')
|
||||
parser.add_argument('-a', '--abs', action='store_true', dest='abs', default=False, help='compute absolute values')
|
||||
parser.add_argument('-nd', '--no_demean', action='store_true', default=False, help='do not demean travel times')
|
||||
parser.add_argument('-nc', '--no_colorbar', action='store_true', default=False, help='do not plot colorbar')
|
||||
parser.add_argument('-nt', '--no_title', action='store_true', default=False, help='do not plot title')
|
||||
parser.add_argument('-no', '--no_otimes', action='store_true', default=False, help='do not plot relative to otimes')
|
||||
parser.add_argument('-oo', '--only_otimes', action='store_true', default=False, help='only plot observed times (otimes)')
|
||||
parser.add_argument('-i', '--ids', dest='source_ids', type=int, default=[], nargs='*',
|
||||
help='Plot only one source with FMTOMO internal id(s).')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
savefig_dir = 'residual_maps_out' if args.write else ''
|
||||
plot_map(args.otimes, args.sources, args.receivers, args.sourcefile, arrivals_fname=args.arrivals,
|
||||
rtimes_fname=args.rtimes, stack=args.stack, source_ids=args.source_ids, colorbar=not args.no_colorbar,
|
||||
title=not args.no_title, savefig_dir=savefig_dir, demean=not args.no_demean, absolute=args.abs,
|
||||
file_ext=args.fext, relative_to_otimes=not args.no_otimes, only_otimes= args.only_otimes)
|
||||
@@ -1,270 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
'''
|
||||
Estimate ray crossing at each depth using rays (as npy objects) precalculated from FMTOMO file rays.dat
|
||||
(e.g. script: plot_rays_on_plane)
|
||||
'''
|
||||
import os
|
||||
import glob
|
||||
import json
|
||||
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
from scipy.interpolate import RegularGridInterpolator
|
||||
|
||||
from pylot.tomography.fmtomo_tools.fmtomo_grid_utils import read_vgrid_regular, read_vgrid
|
||||
from pylot.tomography.map_utils import angle_marker, make_map
|
||||
|
||||
pjoin = os.path.join
|
||||
|
||||
|
||||
def main(fdir, it=1, station_coords_file='/data/AdriaArray_Data/various/station_coords.json'):
|
||||
fnin_vgrid = pjoin(fdir, f'it_{it}/vgrids.in')
|
||||
fnout_vtk = pjoin(fdir, f'vgrids_it{it}_w_ray_crossings.vtk')
|
||||
fnout_npy = pjoin(fdir, f'vgrids_hf_it{it}' + '_crossing_bins_{}-{}_mincount_{}_STEP_{}.npy')
|
||||
|
||||
# binsize (degrees), min_count for each direction to raise quality by 1 increment
|
||||
binsize_az = 90.
|
||||
binsize_incl = 60.
|
||||
min_count = 5
|
||||
plot = False
|
||||
write = True
|
||||
draw_hists = True
|
||||
draw_simple_hists = True
|
||||
step = 2 # take nth sample of vgrid for calculating cells
|
||||
|
||||
# for stations plotting
|
||||
with open(station_coords_file, 'r') as infile:
|
||||
stations = json.load(infile)
|
||||
lonsStations, latsStations = zip(*[(sta['longitude'], sta['latitude']) for sta in stations.values()])
|
||||
|
||||
# some constants etc.
|
||||
R = 6371.
|
||||
bins_azim = np.arange(0, 360 + binsize_az, binsize_az)
|
||||
bins_incl = np.arange(0., 60. + binsize_incl, binsize_incl)
|
||||
nbins_azim = len(bins_azim) - 1
|
||||
nbins_incl = len(bins_incl) - 1
|
||||
|
||||
# for plotting:
|
||||
bins = bins_azim
|
||||
binsize = binsize_az
|
||||
nbins = nbins_azim
|
||||
|
||||
# get vgrid and estimate dlat/dlon
|
||||
vgrid_reg = read_vgrid_regular(fnin_vgrid)
|
||||
lons, lats, rads = [array[::step] for array in vgrid_reg[0]]
|
||||
vps, covs, sms, pdvs = [array[::step, ::step, ::step] for array in vgrid_reg[1:]]
|
||||
|
||||
dlat = lats[1] - lats[0]
|
||||
dlon = lons[1] - lons[0]
|
||||
|
||||
# grid = init_dict()
|
||||
# grid['vps'] = list(vps.ravel())
|
||||
# grid['covs'] = list(covs.ravel())
|
||||
# grid['sms'] = list(sms.ravel())
|
||||
# grid['pdvs'] = list(pdvs.ravel())
|
||||
# grid['res'] = []
|
||||
#
|
||||
# for rad in rads:
|
||||
# for lat in lats:
|
||||
# for lon in lons:
|
||||
# depth = R - rad
|
||||
# grid['lons'].append(lon)
|
||||
# grid['lats'].append(lat)
|
||||
# grid['depths'].append(depth)
|
||||
# x, y, z = pol2cart(lat, lon, R - depth)
|
||||
# grid['xs'].append(x)
|
||||
# grid['ys'].append(y)
|
||||
# grid['zs'].append(z)
|
||||
|
||||
# just for plotting at certain depths (not for write!!!)
|
||||
depths = np.arange(0., 800., 100)
|
||||
if not write:
|
||||
rads = R - depths
|
||||
|
||||
resolutions = []
|
||||
lons_export = []
|
||||
lats_export = []
|
||||
rads_export = []
|
||||
|
||||
print(rads)
|
||||
for rad in rads:
|
||||
# prepare dict containing horizontal angle for key tuple (ilat, ilon)
|
||||
vgrid_azim_ids = {}
|
||||
vgrid_incl_ids = {}
|
||||
|
||||
print('Working on radius', rad)
|
||||
# iterate over rays
|
||||
for fnin_rays in glob.glob1(fpath_events, '*.npz'):
|
||||
rays = np.load(os.path.join(fpath_events, fnin_rays), allow_pickle=True)
|
||||
# raypoints = np.zeros((len(rays), 3))
|
||||
#for index in range(len(rays)):
|
||||
for ray in rays.values():
|
||||
#ray = rays[index]
|
||||
# get index of closest depth here from ray (TAKE CARE OF ALIASING! Using nearest value)
|
||||
ind_min = np.abs(ray[:, 0] - rad).argmin()
|
||||
# in case ind_min is at upper boundary (surface)
|
||||
if ind_min == len(ray[:, 0]) - 1:
|
||||
ind_min -= 1
|
||||
# get diffs to following ray index (km, deg, deg)!
|
||||
ray_diff = np.diff(ray[ind_min:ind_min + 2], axis=0)[0]
|
||||
lat = ray[ind_min, 1]
|
||||
lat_diff_km = ray_diff[1] * (np.pi * R) / 180.
|
||||
lon_diff_km = ray_diff[2] * (np.pi * np.cos(np.deg2rad(lat)) * R) / 180.
|
||||
r_diff_km = ray_diff[0]
|
||||
dlateral = np.sqrt(lat_diff_km ** 2 + lon_diff_km ** 2)
|
||||
# calculate horizontal angle
|
||||
azim = np.rad2deg(np.arctan2(lon_diff_km, lat_diff_km))
|
||||
incl = np.rad2deg(np.arctan(dlateral / r_diff_km))
|
||||
# correct angle from -180-180 to 0-360 and also change azim to bazim
|
||||
bazim = azim + 180.
|
||||
# angles[index] = angle
|
||||
# raypoints[index] = ray[ind_min]
|
||||
lat, lon = ray[ind_min, 1:]
|
||||
lati = np.where((lats <= lat + dlat / 2.) & (lats > lat - dlat / 2.))[0][0]
|
||||
loni = np.where((lons <= lon + dlon / 2.) & (lons > lon - dlon / 2.))[0][0]
|
||||
key = (lati, loni)
|
||||
if not key in vgrid_azim_ids.keys():
|
||||
vgrid_azim_ids[key] = []
|
||||
vgrid_incl_ids[key] = []
|
||||
vgrid_azim_ids[key].append(bazim)
|
||||
vgrid_incl_ids[key].append(incl)
|
||||
# vgrid_ids[index] = np.array([lati, loni])
|
||||
# plt.scatter(lons[loni], lats[lati], c='r', marker='o', facecolor='none', alpha=0.5)
|
||||
# sc = plt.scatter(raypoints[:,2], raypoints[:,1], c=angles[:])
|
||||
|
||||
vgrid_angles_quality = {}
|
||||
vgrid_azim_hist = {}
|
||||
|
||||
for inds in vgrid_azim_ids.keys():
|
||||
azims = vgrid_azim_ids[inds]
|
||||
incls = vgrid_incl_ids[inds]
|
||||
lati, loni = inds
|
||||
hist_az, _ = np.histogram(azims, bins=bins_azim)
|
||||
hist_incl, _ = np.histogram(incls, bins=bins_incl)
|
||||
hist2d, _, _ = np.histogram2d(azims, incls, bins=[bins_azim, bins_incl])
|
||||
# hist_az, hist_inc = hist2d
|
||||
hist = hist2d.ravel()
|
||||
quality = len(hist[hist > min_count - 1]) / (nbins_azim * nbins_incl)
|
||||
# quality = len(hist_az[hist_az > min_count - 1]) / nbins_azim
|
||||
# quality = len(hist_incl[hist_incl > min_count - 1]) / nbins_incl
|
||||
vgrid_angles_quality[inds] = quality
|
||||
vgrid_azim_hist[inds] = hist_az
|
||||
|
||||
# LATS = []
|
||||
# LONS = []
|
||||
qualities = []
|
||||
hists = []
|
||||
|
||||
for ilat, lat in enumerate(lats):
|
||||
for ilon, lon in enumerate(lons):
|
||||
# LATS.append(lat)
|
||||
# LONS.append(lon)
|
||||
key = (ilat, ilon)
|
||||
quality = vgrid_angles_quality.get(key)
|
||||
hist = vgrid_azim_hist.get(key)
|
||||
# print(key, quality)
|
||||
if not quality:
|
||||
quality = 0.
|
||||
# hist for plotting only!
|
||||
if hist is None:
|
||||
hist = np.zeros(nbins)
|
||||
qualities.append(quality)
|
||||
hists.append(hist)
|
||||
lons_export.append(lon)
|
||||
lats_export.append(lat)
|
||||
rads_export.append(rad)
|
||||
|
||||
resolutions += qualities
|
||||
|
||||
if plot:
|
||||
# TODO: still old basemap code
|
||||
raise NotImplementedError('Still using old basemap code')
|
||||
fig = plt.figure()
|
||||
ax = fig.add_subplot(111)
|
||||
bmap = make_map(ax, resolution='l')
|
||||
|
||||
LONS, LATS = np.meshgrid(lons, lats)
|
||||
|
||||
sc = bmap.pcolormesh(LONS - dlon / 2., LATS - dlat / 2.,
|
||||
np.array(qualities).reshape(LATS.shape), latlon=True, zorder=1.5,
|
||||
shading='nearest') # , s=np.array(qualities)*100)
|
||||
|
||||
# sc = plt.contourf(LONS, LATS, np.array(qualities).reshape(LATS.shape), levels=21)
|
||||
|
||||
if draw_hists:
|
||||
for index, bin in enumerate(bins[:-1]):
|
||||
marker = angle_marker(bin, bin + binsize)
|
||||
s = np.array(hists)[:, index]
|
||||
if draw_simple_hists:
|
||||
s[s < min_count] = 0
|
||||
s[s >= min_count] = 150
|
||||
else:
|
||||
s *= 10.
|
||||
sc_angle = bmap.scatter(LONS, LATS, s=s, marker=marker, edgecolors='0.75', alpha=1., linewidths=0.6,
|
||||
latlon=True, zorder=1.5, )
|
||||
sc_angle.set_facecolor('none')
|
||||
# sc_angle = plt.scatter(LONS, LATS, s=1, marker='.', edgecolors='1.', alpha=1., linewidths=1.)
|
||||
|
||||
bmap.scatter(lonsStations, latsStations, c='k', s=0.5, latlon=True, zorder=1.5, ) # , alpha=0.5)
|
||||
plt.title('Azimuthal coverage at depth of {}km, {} bins'.format((R - rad), nbins_azim * nbins_incl))
|
||||
# plt.xlim([0, 22])
|
||||
# plt.ylim([40, 53])
|
||||
# plt.gca().set_aspect('equal')
|
||||
cbar = plt.colorbar(sc)
|
||||
cbar.set_label('Azimuthal coverage')
|
||||
plt.show()
|
||||
|
||||
if write:
|
||||
rginter_res = RegularGridInterpolator((rads, lats, lons),
|
||||
np.array(resolutions).reshape((len(rads), len(lats), len(lons))),
|
||||
bounds_error=False, fill_value=0.)
|
||||
grid = read_vgrid(fnin_vgrid)[0]
|
||||
grid['res'] = []
|
||||
for lon, lat, depth in zip(grid['lons'], grid['lats'], grid['depths']):
|
||||
grid['res'].append(rginter_res((R - depth, lat, lon)))
|
||||
a = np.array([lons_export, lats_export, rads_export, resolutions])
|
||||
np.save(fnout_npy.format(nbins_azim, nbins_incl, min_count, step), a)
|
||||
write_vtk(grid, fnout_vtk,
|
||||
write_data=['vps', 'res', 'covs', 'frechs']) # , clon=11., clat=46., dlon=12., dlat=6., sort=True)
|
||||
|
||||
|
||||
def rays_to_npy(infile, fnout_npy, n_points=10):
|
||||
rays = {}
|
||||
i = 0
|
||||
src_id_old = 1
|
||||
while True:
|
||||
i += 1
|
||||
l1 = infile.readline()
|
||||
if l1 == '': break
|
||||
rec_id, src_id, ray_id = [int(item) for item in l1.split()[:3]]
|
||||
if not src_id in rays.keys():
|
||||
rays[src_id] = []
|
||||
l2 = infile.readline()
|
||||
n = int(l2.split()[0])
|
||||
ray = np.zeros((n, 3))
|
||||
for index in range(n):
|
||||
r, lat, lon = [float(item) for item in infile.readline().split()]
|
||||
ray[index] = np.array([r, np.rad2deg(lat), np.rad2deg(lon)])
|
||||
rays[src_id].append(ray[::n_points])
|
||||
|
||||
dirname = os.path.split(fnout_npy)[0]
|
||||
if not os.path.isdir(dirname):
|
||||
os.mkdir(dirname)
|
||||
|
||||
for src_id, ray in rays.items():
|
||||
np.savez(fnout_npy.format(src_id), *ray)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
fmtomodir = '/data/AdriaArray_Data/fmtomo_adriaarray/alpadege/crust_incl_TESAURO_sm30_dm1/'
|
||||
it = 12
|
||||
|
||||
fpath_events = pjoin(fmtomodir, f'rays_npy_it{it}/')
|
||||
infile = open(pjoin(fmtomodir, f'it_{it}/rays.dat'), 'r')
|
||||
fnout_npy = pjoin(fpath_events, f'it_{it}_rays_event_' + '{}.npz')
|
||||
|
||||
rays_to_npy(infile, fnout_npy)
|
||||
|
||||
main(fmtomodir, it=it)
|
||||
@@ -1,43 +0,0 @@
|
||||
import os
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
fpaths = ['/data/AlpArray_Data/fmtomo/v6/fwi_model_wolfgang_test',
|
||||
'/data/AlpArray_Data/fmtomo/v6/final']
|
||||
arr_path_ref = 'rtimes_tele.dat'
|
||||
|
||||
iterations = [0, 1, 2, 3, 4]
|
||||
labels = ['rel_1d'] + [f'rel_it{it}' for it in iterations]
|
||||
|
||||
colors = plt.get_cmap('viridis')(np.linspace(0,1, len(iterations)))
|
||||
colors = np.vstack(((.5, .5, .5, 1), colors))
|
||||
|
||||
axes = []
|
||||
for fpath in fpaths:
|
||||
rtimes = np.genfromtxt(os.path.join(fpath, arr_path_ref))[:, 4]
|
||||
otimes = np.genfromtxt(os.path.join(fpath, 'otimes.dat'), skip_header=1)[:, 4]
|
||||
|
||||
res = np.empty((len(rtimes), len(iterations) + 1))
|
||||
|
||||
res[:, 0] = otimes # = otimes + rtimes - rtimes
|
||||
|
||||
for index, itr in enumerate(iterations):
|
||||
arr_path = f'it_{itr}/arrivals.dat'
|
||||
|
||||
arrivals = np.genfromtxt(os.path.join(fpath, arr_path))[:, 4]
|
||||
res[:, index + 1] = (otimes + rtimes) - arrivals # otimes (rel) + rtimes => otimes (abs)
|
||||
|
||||
fig = plt.figure()
|
||||
ax = fig.add_subplot(111)
|
||||
axes.append(ax)
|
||||
ax.hist(res, bins=np.linspace(-5, 5, 50), rwidth=0.9, color=colors, label=labels)
|
||||
ax.set_xlim(-1.5, 1.5)
|
||||
plt.title(fpath)
|
||||
|
||||
ax.legend()
|
||||
|
||||
ylim_max = max([ax.get_ylim()[1] for ax in axes])
|
||||
for ax in axes:
|
||||
ax.set_ylim(0, ylim_max)
|
||||
|
||||
plt.show()
|
||||
@@ -1,187 +0,0 @@
|
||||
# This script estimates density of station distribution using Gaussian KDE. For each point a Gaussian Kernel with
|
||||
# sigma ~ grid size (?) is summed in 2D, calculating estimate density function. If the sum of all points evaluated
|
||||
# at the data points increases that means there are more regions with high density
|
||||
|
||||
import os
|
||||
import copy
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
from scipy.stats import gaussian_kde
|
||||
|
||||
from pylot.tomography.fmtomo_tools.fmtomo_teleseismic_utils import organize_receivers, organize_event_names, export_otimes
|
||||
|
||||
def get_events_dict(otimes, recs):
|
||||
events_dict = {}
|
||||
for otime in otimes:
|
||||
rec_id, src_id = int(otime[0]), int(otime[1])
|
||||
if not src_id in events_dict.keys():
|
||||
events_dict[src_id] = dict(lons=[], lats=[], rec_ids=[])
|
||||
events_dict[src_id]['lons'].append(recs[rec_id]['lon'])
|
||||
events_dict[src_id]['lats'].append(recs[rec_id]['lat'])
|
||||
events_dict[src_id]['rec_ids'].append(rec_id)
|
||||
return events_dict
|
||||
|
||||
|
||||
def transfer_kde_to_weight(kdes):
|
||||
weights = 0.1 - kdes
|
||||
indices_negative = np.where(weights < 0)[0]
|
||||
if indices_negative:
|
||||
print('Warning! {} negative indices in weights. Set to 0.'.format(len(indices_negative)))
|
||||
weights[indices_negative] = 0.
|
||||
return weights
|
||||
|
||||
|
||||
def calc_event_kernels(events_dict, eventnames, kde_factor, plot_single=False, fdir_out=None):
|
||||
kernels = {}
|
||||
evaluated_kernels = {}
|
||||
|
||||
if plot_single:
|
||||
fig = plt.figure(figsize=(16, 9))
|
||||
|
||||
for index in range(len(eventnames)):
|
||||
eventname = eventnames[index]
|
||||
source = index + 1
|
||||
lons = events_dict[source]['lons']
|
||||
lats = events_dict[source]['lats']
|
||||
kernel = gaussian_kde(np.array([lons, lats]), bw_method=kde_factor)
|
||||
evaluated_kernel = kernel((lons, lats))
|
||||
kernels[source] = kernel
|
||||
evaluated_kernels[source] = evaluated_kernel
|
||||
if plot_single:
|
||||
assert fdir_out, 'Need to specify output directory for plots'
|
||||
if not os.path.isdir(fdir_out):
|
||||
os.mkdir(fdir_out)
|
||||
weights = transfer_kde_to_weight(evaluated_kernel)
|
||||
iterdict = {'kde': evaluated_kernel, 'weight': weights}
|
||||
for name, colorarray in iterdict.items():
|
||||
if name == 'kde':
|
||||
vmin=0
|
||||
vmax=0.05
|
||||
else:
|
||||
vmin=None
|
||||
vmax=None
|
||||
scs = plt.scatter(lons, lats, marker='o', c=colorarray, lw=1.0, vmin=vmin, vmax=vmax)
|
||||
cbar = plt.colorbar(scs)
|
||||
plt.title('Station {} for event {}'.format(name, eventname))
|
||||
fig.savefig(os.path.join(fdir_out, '{}_{}.svg'.format(eventname, name)))
|
||||
fig.clear()
|
||||
#plt.show()
|
||||
if plot_single:
|
||||
plt.close('all')
|
||||
return kernels, evaluated_kernels
|
||||
|
||||
|
||||
def plot_hist_kernels(evaluated_kernels):
|
||||
all_kernels = np.array([])
|
||||
for kernel in evaluated_kernels.values():
|
||||
all_kernels = np.append(all_kernels, kernel)
|
||||
plt.hist(all_kernels, bins=200, label='kernels')
|
||||
plt.title('Distribution of all kernels.')
|
||||
plt.show()
|
||||
|
||||
|
||||
def plot_hist_uncertainties(otimes_new, otimes_orig, bins=200):
|
||||
plt.hist(otimes_orig[:, -1], lw=1, bins=bins, label='Unmodified uncertainties', fc=(.5, .5, 0., 0.5))
|
||||
plt.hist(otimes_new[:, -1], lw=1, bins=bins, label='New uncertainties', fc=(0., .5, .5, 0.5))
|
||||
plt.legend()
|
||||
plt.xlabel('Uncertainty [s]')
|
||||
plt.title('Uncertainty distribution before and after correction.')
|
||||
plt.show()
|
||||
|
||||
|
||||
def plot_uncertainties(otimes, recs, eventnames, fdir_out, vmin=0.1, vmax=0.4):
|
||||
if not os.path.isdir(fdir_out):
|
||||
os.mkdir(fdir_out)
|
||||
|
||||
print('Writing output to directory:', fdir_out)
|
||||
epsilon = 1e-6
|
||||
|
||||
fig = plt.figure(figsize=(16, 9))
|
||||
for index, eventname in enumerate(eventnames):
|
||||
src_id = index + 1
|
||||
indices = np.where(abs(otimes[:, 1] - src_id) <= epsilon)
|
||||
rec_ids = otimes[:, 0][indices]
|
||||
uncs = otimes[:, -1][indices]
|
||||
lats = [recs[rec_id]['lat'] for rec_id in rec_ids]
|
||||
lons = [recs[rec_id]['lon'] for rec_id in rec_ids]
|
||||
sc = plt.scatter(lons, lats, c=uncs, vmin=vmin, vmax=vmax)
|
||||
plt.title(eventname)
|
||||
cb = plt.colorbar(sc)
|
||||
fig.savefig(os.path.join(fdir_out, '{}.svg'.format(eventname)))
|
||||
fig.clear()
|
||||
plt.close('all')
|
||||
|
||||
|
||||
def exp_func(uncertainty, eval_kernel, exponent=30):
|
||||
return uncertainty * np.exp(exponent * eval_kernel)
|
||||
|
||||
|
||||
def modify_otimes(otimes, recs, kernels):
|
||||
print('Applying Kernel on otimes and modifying uncertainties...')
|
||||
for otime in otimes:
|
||||
rec_id, src_id = int(otime[0]), int(otime[1])
|
||||
lon = recs[rec_id]['lon']
|
||||
lat = recs[rec_id]['lat']
|
||||
eval_kernel = kernels[src_id]((lon, lat))
|
||||
otime[-1] = exp_func(otime[-1], eval_kernel)
|
||||
return otimes
|
||||
|
||||
|
||||
def plot_average_kernel(evaluated_kernels, eventnames):
|
||||
ids_names = [(index + 1, eventname) for index, eventname in enumerate(eventnames)]
|
||||
sorted_events = sorted(ids_names, key=lambda x: x[1])
|
||||
kernel_sums = {src_id: np.sum(kernel) for src_id, kernel in evaluated_kernels.items()}
|
||||
src_ids = [item[0] for item in sorted_events]
|
||||
eventnames = [item[1] for item in sorted_events]
|
||||
sums = [kernel_sums[src_id] for src_id in src_ids]
|
||||
plt.plot(sums)
|
||||
xticks = np.arange(0, len(eventnames), step=len(eventnames)//10)
|
||||
xticklabels = [eventname[:8] for index, eventname in enumerate(eventnames) if index in xticks]
|
||||
plt.xticks(xticks, xticklabels)
|
||||
plt.title('Average kernel per station. High value means inhomogeneous station distribution.')
|
||||
plt.show()
|
||||
|
||||
|
||||
def export_station_kdes(events_dict, evaluated_kernels, fnout):
|
||||
with open(fnout, 'w') as outfile:
|
||||
for src_id in range(1, len(events_dict) + 1):
|
||||
kernel = evaluated_kernels[src_id]
|
||||
for kde, rec_id in zip(kernel, events_dict[src_id]['rec_ids']):
|
||||
outfile.write('{} {} {}\n'.format(rec_id, src_id, kde))
|
||||
|
||||
|
||||
def main(plot=False, plot_detailed=False):
|
||||
working_path = '/rscratch/minos13/marcel/fmtomo_alparray/v4/alparray_mantle_diehl_crust_included_hf_gradient_smoothing'
|
||||
fdir_out = 'station_density'
|
||||
fnout = os.path.join(fdir_out, 'station_kdes.txt')
|
||||
|
||||
os.chdir(working_path)
|
||||
eventnames = organize_event_names('input_source_file_P.in')
|
||||
recs = organize_receivers('receivers.in')
|
||||
otimes_data = np.genfromtxt('otimes_orig.dat', skip_header=1)
|
||||
|
||||
# kernel width ~ grid size? or station spacing?
|
||||
kde_size = 30 # km
|
||||
kde_factor = kde_size / (6371. * np.pi / 180.)
|
||||
print('KDE width (degree):', kde_factor)
|
||||
|
||||
events_dict = get_events_dict(otimes_data, recs)
|
||||
kernels, evaluated_kernels = calc_event_kernels(events_dict, eventnames, kde_factor, plot_single=plot_detailed,
|
||||
fdir_out=fdir_out)
|
||||
|
||||
export_station_kdes(events_dict, evaluated_kernels, fnout)
|
||||
|
||||
otimes_modif = modify_otimes(copy.deepcopy(otimes_data), recs, kernels)
|
||||
#export_otimes(otimes_modif, os.path.join(working_path, 'otimes_modif_kernel.dat'))
|
||||
if plot:
|
||||
plot_average_kernel(evaluated_kernels, eventnames)
|
||||
plot_hist_kernels(evaluated_kernels)
|
||||
plot_hist_uncertainties(otimes_modif, otimes_data)
|
||||
if plot_detailed:
|
||||
plot_uncertainties(otimes_data, recs, eventnames, 'uncerts_old')
|
||||
plot_uncertainties(otimes_modif, recs, eventnames, 'uncerts_new')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main(plot=True, plot_detailed=True)
|
||||
@@ -1,17 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
ulimit -s 8192
|
||||
|
||||
conda activate pylot_311
|
||||
|
||||
##qsub -l low -cwd -l "os=*stretch" -pe mpi-fu 40 submit_fmtomo.sh
|
||||
#$ -l low
|
||||
#$ -cwd
|
||||
#$ -pe smp 40
|
||||
#$ -q "*@minos11, *@minos12, *@minos15"
|
||||
|
||||
export PYTHONPATH="$PYTHONPATH:/home/marcel/git/"
|
||||
export PYTHONPATH="$PYTHONPATH:/home/marcel/git/pylot/"
|
||||
export PYTHONPATH="$PYTHONPATH:/home/marcel/git/pylot_tools/"
|
||||
|
||||
python fmtomo.py
|
||||
@@ -1,10 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
conda activate py37
|
||||
|
||||
#qsub -l low -cwd -l "os=*stretch" -pe mpi-fu 40 submit_fmtomo_grid_utils.sh
|
||||
|
||||
export PYTHONPATH="$PYTHONPATH:/home/marcel/git/"
|
||||
export PYTHONPATH="$PYTHONPATH:/home/marcel/git/pylot_tools/"
|
||||
|
||||
python fmtomo_grid_utils.py
|
||||
@@ -1,21 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import os
|
||||
from pylot.tomography.fmtomo_utils import Tomo3d
|
||||
|
||||
#tomo = Tomo3d('/rscratch/minos13/marcel/fmtomo_alparray/alparray_mantle_from_m6.0_tesauro_model_on_top',
|
||||
# '/rscratch/minos13/marcel/fmtomo_alparray/alparray_mantle_from_m6.0_tesauro_model_on_top',
|
||||
# buildObs=False, saveRays=False)
|
||||
|
||||
wdir = '/rscratch/minos13/marcel/fmtomo_alparray/'
|
||||
|
||||
#directories = ['alparray_mantle_from_m6.0_diehl_crustal_corrections_sm1_damp3',
|
||||
#'alparray_mantle_from_m6.0_diehl_crustal_corrections_sm1_damp30',]
|
||||
directories= ['alparray_mantle_from_m6.0_diehl_crustal_corrections_sm10_damp3',
|
||||
'alparray_mantle_from_m6.0_diehl_crustal_corrections_sm10_damp30']
|
||||
|
||||
for dire in directories:
|
||||
path = os.path.join(wdir, dire)
|
||||
tomo = Tomo3d(path, path, buildObs=False, saveRays=False)
|
||||
tomo.runTOMO3D(40, 8)
|
||||
@@ -1,12 +0,0 @@
|
||||
#!/bin/bash
|
||||
ulimit -s 8192
|
||||
|
||||
#$ -l low,os=*stretch
|
||||
#$ -l h_vmem=5G
|
||||
#$ -cwd
|
||||
#$ -pe smp 40
|
||||
|
||||
export PYTHONPATH="$PYTHONPATH:/home/marcel/git/"
|
||||
export PYTHONPATH="$PYTHONPATH:/home/marcel/git/pylot_tools/"
|
||||
|
||||
python submit_fmtomo_run.py
|
||||
@@ -1,62 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
ulimit -s 8192
|
||||
|
||||
conda activate pylot_311
|
||||
|
||||
##qsub -l low -cwd -l "os=*stretch" -pe mpi-fu 40 submit_fmtomo_teleseismic.sh
|
||||
#$ -l low
|
||||
##$ -l os=*stretch
|
||||
#$ -cwd
|
||||
#$ -pe smp 40
|
||||
#$ -q "*@minos11, *@minos12, *@minos15"
|
||||
### WARNING fm3d_prepare_tele not compiled correctly for all cluster machines (e.g. not working on minos12, minos15)
|
||||
|
||||
export PYTHONPATH="$PYTHONPATH:/home/marcel/git/"
|
||||
export PYTHONPATH="$PYTHONPATH:/home/marcel/git/pylot/"
|
||||
export PYTHONPATH="$PYTHONPATH:/home/marcel/git/pylot_tools/"
|
||||
|
||||
#python fmtomo_teleseismic.py '/data/AlpArray_Data/dmt_database_mantle_0.03-0.1_revised' \
|
||||
#'/data/AlpArray_Data/fmtomo/v6/crust_incl_lf_sm_FIX_DTS_grad_sm30_dm10' \
|
||||
#'_correlated_0.03-0.1' -n $NSLOTS --blacklist '/rscratch/minos22/marcel/alparray/station_blacklist.csv'
|
||||
# --model 'ak135_diehl_v2'
|
||||
|
||||
#python fmtomo_teleseismic.py '/data/AlpArray_Data/dmt_database_mantle_0.03-0.5_revised' \
|
||||
#'/data/AlpArray_Data/fmtomo/v6/crust_incl_hf_sm_FIX_DTS_grad_sm30_dm10' \
|
||||
#'_correlated_0.03-0.5' -n $NSLOTS --blacklist '/rscratch/minos22/marcel/alparray/station_blacklist.csv'
|
||||
# --model 'ak135_diehl_v2'
|
||||
|
||||
#python fmtomo_teleseismic.py '/data/AlpArray_Data/dmt_database_mantle_0.01-0.2_S_SKS' \
|
||||
#'/data/AlpArray_Data/fmtomo/v6_S/crust_incl_hf_KSTL_grad_sm6_dm10/' \
|
||||
#'_correlated_0.01-0.2_S*' -n $NSLOTS --blacklist '/data/AlpArray_Data/various/alparray/station_blacklist.csv'
|
||||
# --model 'ak135_diehl_v2'
|
||||
#
|
||||
#python fmtomo_teleseismic.py '/data/AlpArray_Data/dmt_database_mantle_0.03-0.5_revised' \
|
||||
# '/rscratch/minos13/marcel/fmtomo_alparray/v3.5/alparray_mantle_diehl_crust_included_hf_no_init' \
|
||||
# '_correlated_0.03-0.5' -n $NSLOTS --no_write_init_nodes --model 'ak135_diehl_v2'
|
||||
|
||||
#python fmtomo_teleseismic.py '/data/AlpArray_Data/dmt_database_hybrid_test' \
|
||||
# '/rscratch/minos13/marcel/fmtomo_alparray/v3/hybrid_method_test_pgrid_fine' \
|
||||
# '' -n $NSLOTS #--blacklist '/rscratch/minos22/marcel/alparray/station_blacklist.csv' --model 'ak135_diehl_v2'
|
||||
|
||||
#python fmtomo_teleseismic.py '/data/AlpArray_Data/dmt_database_hybrid_test_diehl_v2' \
|
||||
#'/data/AlpArray_Data/fmtomo/v6/hybrid_method_test' \
|
||||
#'' -n $NSLOTS --model 'ak135_diehl_v2'
|
||||
#--blacklist '/rscratch/minos22/marcel/alparray/station_blacklist.csv'
|
||||
|
||||
#python fmtomo_teleseismic.py '/data/AlpArray_Data/dmt_database_hybrid_test_diehl_v2_orig_box_more_stations' \
|
||||
#'/data/AlpArray_Data/fmtomo/v5/hybrid_method_tests_diss/v5_orig_box_size_more_receiver_no_demean_CORRECTED_depth_sampling' \
|
||||
#'' -n $NSLOTS --model 'ak135_diehl_v2'
|
||||
|
||||
#python fmtomo_teleseismic.py '/data/AlpArray_Data/dmt_database_synth_model_mk6_it3_no_rotation' \
|
||||
#'/data/AlpArray_Data/fmtomo/v6_resolution_analysis/fwi_mk6_it3_P' \
|
||||
#'_correlated_P' -n $NSLOTS --model 'ak135_diehl_v2'
|
||||
|
||||
#python fmtomo_teleseismic.py '/data/AdriaArray_Data/dmt_database_alpadege' \
|
||||
#'/data/AdriaArray_Data/fmtomo_adriaarray/alpadege/no_crust_correction' \
|
||||
#'_correlated_0.03-0.5' -n $NSLOTS --model 'ak135'
|
||||
|
||||
python fmtomo_teleseismic.py '/data/AdriaArray_Data/dmt_database_minimal' \
|
||||
'/data/AdriaArray_Data/fmtomo_adriaarray/alpadege/test_minimal' \
|
||||
'_correlated_0.03-0.5' -n $NSLOTS --model 'ak135'
|
||||
|
||||
@@ -1,266 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
import os
|
||||
import glob
|
||||
import subprocess
|
||||
|
||||
import json
|
||||
|
||||
import numpy as np
|
||||
import numpy.polynomial.polynomial as poly
|
||||
|
||||
from scipy.sparse import spdiags
|
||||
from scipy.optimize import curve_fit
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
from itertools import cycle
|
||||
|
||||
from pylot.tomography.fmtomo_tools.fmtomo_grid_utils import read_vgrid
|
||||
|
||||
# def calc_dampnorm(vgrid, vgrid_ref):
|
||||
# # calculate m - m0
|
||||
# m_m0 = np.array(vgrid['vps']) - np.array(vgrid_ref['vps'])
|
||||
#
|
||||
# # calculate inverse of diagonal elements of a priori model covariance matrix (which should be a diagonal matrix)
|
||||
# # IMPORTANT: COVARIANCES ARE MOST LIKELY STANDARD DEVIATIONS -> square them
|
||||
# covs = np.array(vgrid_ref['covs'])**2
|
||||
#
|
||||
# covs_inv = 1. / covs
|
||||
#
|
||||
# #cm_inv = spdiags(covs_inv, 0, covs_inv.size, covs_inv.size)#
|
||||
#
|
||||
# #norm_calc_old = np.dot(m_m0.transpose(), m_m0 * cm_inv)
|
||||
#
|
||||
# norm = np.dot(m_m0, m_m0 * covs_inv)
|
||||
#
|
||||
# return norm
|
||||
#
|
||||
#
|
||||
# def calc_smoothnorm(vgrid, gridn, R=6371.):
|
||||
# nR, nTheta, nPhi = gridn
|
||||
#
|
||||
# vps = np.array(vgrid['vps'])
|
||||
# lats = np.array(vgrid['lats'])
|
||||
# lons = np.array(vgrid['lons'])
|
||||
# depths = np.array(vgrid['depths'])
|
||||
# #vgridref = np.array(vgridref['vps'])
|
||||
# #dvgrid = vgrid - vgridref
|
||||
#
|
||||
# vgarray = np.zeros((nR, nTheta, nPhi))
|
||||
# lonsarray_km = np.zeros((nR, nTheta, nPhi))
|
||||
# latsarray_km = np.zeros((nR, nTheta, nPhi))
|
||||
# radsarray_km = np.zeros((nR, nTheta, nPhi))
|
||||
# #vgarray_diff = np.zeros((nR, nTheta, nPhi))
|
||||
# smootharray = np.zeros((nR, nTheta, nPhi))
|
||||
# #for iLayer in range(nlayers):
|
||||
# globInd = 0
|
||||
# for iR in range(nR):
|
||||
# for iTheta in range(nTheta):
|
||||
# for iPhi in range(nPhi):
|
||||
# r = R - depths[globInd]
|
||||
# lat = lats[globInd]
|
||||
# lon = lons[globInd]
|
||||
# r_minor = np.cos(np.deg2rad(lat)) * r
|
||||
# vgarray[iR, iTheta, iPhi] = vps[globInd]
|
||||
# radsarray_km[iR, iTheta, iPhi] = r
|
||||
# latsarray_km[iR, iTheta, iPhi] = np.pi * r * lat / 180.
|
||||
# lonsarray_km[iR, iTheta, iPhi] = np.pi * r_minor * lon / 180.
|
||||
# #vgarray_diff[iR, iTheta, iPhi] = vgrid[globInd]
|
||||
# globInd += 1
|
||||
#
|
||||
# # iterate over grid diffs (correct?) and sum 1 * point left -2 * point + 1 * point right in all 3 dim.
|
||||
# smsum = 0.
|
||||
# for iR in range(nR):
|
||||
# for iTheta in range(nTheta):
|
||||
# for iPhi in range(nPhi):
|
||||
# vg = vgarray[iR, iTheta, iPhi]
|
||||
# sum1 = sum2 = sum3 = 0.
|
||||
# if 0 < iPhi < nPhi - 1:
|
||||
# h = abs(lonsarray_km[iR, iTheta, iPhi + 1] - lonsarray_km[iR, iTheta, iPhi - 1]) / 2
|
||||
# sum1 = (vgarray[iR, iTheta, iPhi - 1] - 2 * vg + vgarray[iR, iTheta, iPhi + 1]) / h**2
|
||||
# if 0 < iTheta < nTheta - 1:
|
||||
# h = abs(latsarray_km[iR, iTheta + 1, iPhi] - latsarray_km[iR, iTheta - 1, iPhi]) / 2
|
||||
# sum2 = (vgarray[iR, iTheta - 1, iPhi] - 2 * vg + vgarray[iR, iTheta + 1, iPhi]) / h**2
|
||||
# if 0 < iR < nR - 1:
|
||||
# h = abs(radsarray_km[iR - 1, iTheta, iPhi] - radsarray_km[iR + 1, iTheta, iPhi]) / 2
|
||||
# sum3 = (vgarray[iR - 1, iTheta, iPhi] - 2 * vg + vgarray[iR + 1, iTheta, iPhi]) / h**2
|
||||
# smsum += np.sqrt(sum1**2 + sum2**2 + sum3**2)
|
||||
# #print(sum1, sum2, sum3, smsum)
|
||||
# smootharray[iR, iTheta, iPhi] = smsum#sum1 + sum2 + sum3
|
||||
#
|
||||
# # m_T * D_T * D * m ?? todo: unsure
|
||||
# norm = np.sum(smootharray ** 2)
|
||||
#
|
||||
# return norm, smootharray
|
||||
from pylot.tomography.utils import normed_figure
|
||||
|
||||
|
||||
def calc_smoothnorm(wdir, iter):
|
||||
smv = np.loadtxt(os.path.join(wdir, 'it_{}/smv.out'.format(iter + 1)), skiprows=1)
|
||||
dm = np.loadtxt(os.path.join(wdir, 'it_{}/dm.out'.format(iter + 1)), skiprows=1)
|
||||
norm = np.sum(smv*dm)
|
||||
return norm
|
||||
|
||||
|
||||
def calc_dampnorm(wdir, iter):
|
||||
ecmi = np.loadtxt(os.path.join(wdir, 'it_{}/ecmi.out'.format(iter + 1)), skiprows=1)
|
||||
dm = np.loadtxt(os.path.join(wdir, 'it_{}/dm.out'.format(iter + 1)), skiprows=1)
|
||||
norm = np.sum(ecmi * dm**2)
|
||||
return norm
|
||||
|
||||
|
||||
def calc_norm(wdir, iteration_number):
|
||||
dampnorm = calc_dampnorm(wdir, iteration_number)
|
||||
smoothnorm = calc_smoothnorm(wdir, iteration_number)
|
||||
|
||||
print('dampnorm: ', dampnorm)
|
||||
print('smoothnorm: ', smoothnorm)
|
||||
|
||||
norm = dampnorm + smoothnorm
|
||||
|
||||
print('Calculated summed norm of', norm)
|
||||
|
||||
return norm, dampnorm, smoothnorm
|
||||
|
||||
|
||||
def calc_tradeoff(fpath_in, fname_out=None, iteration_number = 12):
|
||||
results = {}
|
||||
|
||||
for wdir in glob.glob(fpath_in):
|
||||
#wdir = '/rscratch/minos13/marcel/fmtomo_alparray/alparray_mantle_from_m6.0_diehl_crustal_corrections_sm1000_damp100/'
|
||||
smooth = float(wdir.split('_')[-2].split('sm')[-1])
|
||||
damp = float(wdir.split('_damp')[-1].split('/')[0])
|
||||
|
||||
print('Calculating tradeoff for smoothing and damping of {}, {}'.format(smooth, damp))
|
||||
if not smooth in results.keys():
|
||||
results[smooth] = {}
|
||||
|
||||
iteration_number_new = iteration_number
|
||||
ecmi_path = os.path.join(wdir, 'it_{}'.format(iteration_number_new + 1), 'ecmi.out')
|
||||
smv_path = os.path.join(wdir, 'it_{}'.format(iteration_number_new + 1), 'smv.out')
|
||||
while not os.path.isfile(ecmi_path) or not os.path.isfile(smv_path):
|
||||
iteration_number_new -= 1
|
||||
ecmi_path = os.path.join(wdir, 'it_{}'.format(iteration_number_new + 1), 'ecmi.out')
|
||||
smv_path = os.path.join(wdir, 'it_{}'.format(iteration_number_new + 1), 'smv.out')
|
||||
print('WARNING: Iteration number lowered by 1:', iteration_number_new)
|
||||
if iteration_number_new <= 1:
|
||||
break
|
||||
|
||||
if iteration_number_new <= 1:
|
||||
continue
|
||||
else:
|
||||
iteration_number = iteration_number_new
|
||||
|
||||
#vgrid, gridn, griddelta, gridstart = read_vgrid(vgrid_path)
|
||||
#vgrid_ref = read_vgrid(os.path.join(wdir, 'vgridsref.in'))[0]
|
||||
|
||||
norm, dampnorm, smoothnorm = calc_norm(wdir, iteration_number)
|
||||
|
||||
try:
|
||||
fpath = os.path.join(wdir, 'residuals.dat')
|
||||
chi = float(subprocess.check_output(['tail', fpath]).split()[-1])
|
||||
except Exception as e:
|
||||
print(e)
|
||||
chi = np.nan
|
||||
|
||||
results[smooth][wdir] = {'dampnorm': dampnorm, 'smoothnorm': smoothnorm,
|
||||
'norm': norm, 'chi': chi, 'damp': damp}
|
||||
|
||||
#print some output
|
||||
for smooth, result in results.items():
|
||||
print('Smoothing:', smooth)
|
||||
for wdir, item in result.items():
|
||||
print(item['chi'], item['norm'])
|
||||
print(20*'#')
|
||||
|
||||
if fname_out:
|
||||
with open(fname_out, 'w') as outfile:
|
||||
json.dump(results, outfile)
|
||||
return results
|
||||
|
||||
def quadratic_function(x, a, b, c):
|
||||
return a * x ** 2 + b * x + c
|
||||
|
||||
def one_over_x(x, a, b, c):
|
||||
return a / (x - b) + c
|
||||
|
||||
def exp_func(x, a, b, c):
|
||||
return a * np.exp(-b * x) + c
|
||||
|
||||
def plot_tradeoff(fname_in, fix='smooth', plot_norm='both', min_smooth=0, min_damp=0, max_smooth=1e6, max_damp=1e6):
|
||||
with open(fname_in, 'r') as infile:
|
||||
results_smooth = json.load(infile)
|
||||
|
||||
lines = ["-", "--", "-.", ":"]
|
||||
linecycler = cycle(lines)
|
||||
|
||||
# array will be built for each line: (smooth, damp, norm, chi)
|
||||
plot_values = []
|
||||
for smooth, result in results_smooth.items():
|
||||
for item in result.values():
|
||||
smooth = float(smooth)
|
||||
damping = item['damp']
|
||||
if smooth < min_smooth or damping < min_damp or smooth > max_smooth or damping > max_damp:
|
||||
continue
|
||||
plot_values.append(np.array([smooth, damping, item[plot_norm], item['chi']]))
|
||||
|
||||
plot_values = np.array(plot_values)
|
||||
|
||||
column_index = {'smooth': 0, 'damp': 1}
|
||||
|
||||
keys = np.unique(plot_values[:, column_index[fix]])
|
||||
names = {'smooth': 'Smoothing', 'damp': 'Damping'}
|
||||
|
||||
for key in keys:
|
||||
plot_line = plot_values[plot_values[:, column_index[fix]] == key]
|
||||
second_index = column_index['smooth'] if fix == 'damp' else column_index['damp']
|
||||
plot_line = np.array(sorted(plot_line, key=lambda x: x[second_index]))
|
||||
norms = plot_line[:, 2]
|
||||
chis = plot_line[:, 3]
|
||||
#text = [str(item) for item in plot_line[:, second_index]]
|
||||
|
||||
x = np.linspace(min(norms), max(norms), num=100)
|
||||
|
||||
#popt, pcov = curve_fit(one_over_x, norms, chis, method='trf')#, bounds=[min(norms), max(norms)])
|
||||
#fit_result = one_over_x(x, *popt)
|
||||
#line = plt.plot(x, fit_result, ':', lw=0.8)[0]
|
||||
fninfo = os.path.split(fname_in)[-1].replace('.json', '').split('_f')[-1]
|
||||
label = '{}: {:g}'.format(names[fix], float(key))
|
||||
|
||||
line = plt.plot(norms, chis, linestyle=next(linecycler), lw=0.8, label=label)[0]
|
||||
#coefs = poly.polyfit(norms, chis, 4)
|
||||
#ffit = poly.polyval(x, coefs)
|
||||
#line = plt.plot(x, ffit, ':', lw=0.8)[0]
|
||||
|
||||
#label = label='{}: {:g} (smgrad: {})'.format(names[fix], float(key), fninfo)
|
||||
plt.plot(norms, chis, c=line.get_color(), marker='.', lw=0.)
|
||||
#plt.text(norms, chis, text)
|
||||
for item in plot_line:
|
||||
plt.text(item[2], item[3], str(item[second_index]), horizontalalignment='left')
|
||||
#plt.title('Plot of Misfit against Norm ({})'.format(plot_norm))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
#calc_tradeoff('/data/AlpArray_Data/fmtomo/v5/tradeoff_curves/crust_included_grad_smooth_FIXED_dts_grad_1.5_sm*_damp*/',
|
||||
# '/data/AlpArray_Data/various/alparray/tradeoff_v5_f1.5.json')
|
||||
#calc_tradeoff('/data/AlpArray_Data/fmtomo/v5/tradeoff_curves/crust_included_grad_smooth_FIXED_dts_sm*_damp*/',
|
||||
# '/data/AlpArray_Data/various/alparray/tradeoff_v5_f2.0.json')
|
||||
|
||||
fig = normed_figure(width_cm=10, ratio=1.)
|
||||
#tradeoff_infiles = ['tradeoff_v4_f1.5.json', 'tradeoff_v4_f3.json', 'tradeoff_v4_f10.json']
|
||||
tradeoff_infiles = ['tradeoff_v5_f2.0.json']#, 'tradeoff_v5_f1.5.json']
|
||||
for infile in tradeoff_infiles:
|
||||
infile = os.path.join('/data/AlpArray_Data/various/alparray/', infile)
|
||||
plot_tradeoff(infile, fix='damp', plot_norm='norm')
|
||||
|
||||
plt.xlim([1900, 16200])
|
||||
plt.ylim([2.72, 3.8])
|
||||
plt.xlabel('Norm')
|
||||
#plt.ylabel(r'Misfit($\frac{\chi^2}{N}$)')
|
||||
plt.ylabel(r'Misfit($\chi^2/N$)')
|
||||
#plt.title('Tradeoff curve Misfit vs Norm. Numbers in plot show smoothing values.')
|
||||
plt.legend()
|
||||
#plt.show()
|
||||
plt.savefig('/data/AlpArray_Data/sciebo/AlpArray_home/pictures/paper_II/tradeoff.pdf', dpi=300)
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import numpy as np
|
||||
import json
|
||||
|
||||
from pylot.tomography.fmtomo_tools.fmtomo_grid_utils import read_vgrid, write_vtk, calculate_differences_grid, write_vgrid
|
||||
|
||||
def visualize_frechet_derivative(fname_vgrid, fname_frechet, fname_out_vtk=None, fname_out_json=None,
|
||||
fname_out_vgrid=None, diff_model=None, fname_vgrid_ref=None):
|
||||
vgrid, gridN, gridDelta, gridStart = read_vgrid(fname_vgrid, inv_index_frechet=True)
|
||||
if diff_model and fname_vgrid_ref:
|
||||
raise OverflowError('Cannot have both parameters set, diff_model and fname_vgrid_ref')
|
||||
if diff_model:
|
||||
vgrid = calculate_differences_grid(vgrid, earth_model=diff_model)
|
||||
if fname_vgrid_ref:
|
||||
vgrid_ref, gridN_ref, gridDelta_ref, gridStart_ref = read_vgrid(fname_vgrid_ref, inv_index_frechet=False)
|
||||
grid_check = (gridN == gridN_ref,
|
||||
compare_tuple(gridDelta, gridDelta_ref),
|
||||
compare_tuple(gridStart, gridStart_ref))
|
||||
assert(all(grid_check), 'Missmatch ref grid size')
|
||||
vps = np.array(vgrid['vps'])
|
||||
vps_ref = np.array(vgrid_ref['vps'])
|
||||
vps_rel = (vps - vps_ref) / vps_ref * 100.
|
||||
print('Min/Max change {}/{}%'.format(min(vps_rel), max(vps_rel)))
|
||||
vgrid['vps'] = list(vps_rel)
|
||||
|
||||
add_frechet(vgrid, fname_frechet)
|
||||
|
||||
if fname_out_vgrid:
|
||||
write_vgrid(vgrid, gridN, gridDelta, gridStart, fname_out_vgrid)
|
||||
if fname_out_vtk:
|
||||
write_vtk(vgrid, fname_out_vtk, ['vps', 'frechs', 'grid_indices', 'hit_count'])
|
||||
if fname_out_json:
|
||||
with open(fname_out_vtk, 'w') as outfile:
|
||||
json.dump(vgrid, outfile)
|
||||
|
||||
|
||||
def add_frechet(vgrid, fname_frechet):
|
||||
vgrid['frechs'] = list(np.zeros(len(vgrid['xs'])))
|
||||
vgrid['hit_count'] = list(np.zeros(len(vgrid['xs'])))
|
||||
with open(fname_frechet, 'r') as infile:
|
||||
while True:
|
||||
try:
|
||||
n, source_id, m, k, n_pdev = [int(item) for item in infile.readline().split()]
|
||||
except:
|
||||
break
|
||||
#print(n, source_ids, m, k, n_pdev)
|
||||
for _ in range(n_pdev):
|
||||
pdev_index, pdev = infile.readline().split()
|
||||
pdev_index = int(pdev_index)
|
||||
pdev = float(pdev)
|
||||
vgrid_index = vgrid['inv_index'][pdev_index]
|
||||
vgrid['frechs'][vgrid_index] += pdev
|
||||
# hit by ray count
|
||||
vgrid['hit_count'][vgrid_index] += 1
|
||||
|
||||
|
||||
def compare_tuple(t1, t2, epsilon=1e-6):
|
||||
for item1, item2 in zip(t1, t2):
|
||||
if abs(item1 - item2) > epsilon:
|
||||
return False
|
||||
return True
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,160 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import glob
|
||||
import os
|
||||
import json
|
||||
import numpy as np
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
import cartopy
|
||||
import cartopy.crs as ccrs
|
||||
from cartopy.io.shapereader import Reader
|
||||
from matplotlib.patches import Patch
|
||||
|
||||
#from cmcrameri import cm
|
||||
|
||||
from obspy.geodetics import gps2dist_azimuth
|
||||
|
||||
TRANSFORM = ccrs.PlateCarree()
|
||||
|
||||
|
||||
def draw_schmid_faults(ax, fnin='schmidfaults.shp'):
|
||||
reader = Reader(fnin)
|
||||
lines = []
|
||||
linestyles = []
|
||||
for record in reader.records():
|
||||
info = record.attributes
|
||||
if info["fault_type"] == 1 or info["fault_type"] == 3:
|
||||
linestyles.append('solid')
|
||||
else:
|
||||
linestyles.append('dashed')
|
||||
line_arr = np.array(record.geometry.coords)
|
||||
lines.append(line_arr)
|
||||
# ax.add_collection(LineCollection(lines, linewidths=1.2, linestyles=linemarkers, colors='black', transform=ccrs.PlateCarree()))
|
||||
for line, style in zip(lines, linestyles):
|
||||
ax.plot(line[:, 0], line[:, 1], transform=TRANSFORM, ls=style, c='k', lw=1.2)
|
||||
|
||||
|
||||
def draw_alcapadi_faults(ax, fnin='faults_alcapadi', color='black'):
|
||||
reader = Reader(fnin)
|
||||
lines = []
|
||||
linestyles = []
|
||||
linewidths = []
|
||||
for record in reader.records():
|
||||
info = record.attributes
|
||||
if info["fault_type"] == 1:
|
||||
linestyles.append('solid')
|
||||
linewidths.append(.8)
|
||||
elif info["fault_type"] == 2:
|
||||
linestyles.append('solid')
|
||||
linewidths.append(0.4)
|
||||
else:
|
||||
linestyles.append('dashed')
|
||||
linewidths.append(.8)
|
||||
line_arr = np.array(record.geometry.coords)
|
||||
lines.append(line_arr)
|
||||
for line, style, lwidth in zip(lines, linestyles, linewidths):
|
||||
ax.plot(line[:, 0], line[:, 1], transform=TRANSFORM, ls=style, c='k', lw=lwidth)
|
||||
|
||||
|
||||
def draw_alcapadi_model(ax, fnin='tect_units_alcapadi', alpha=0.2, add_legend=True):
|
||||
reader = Reader(fnin)
|
||||
color_dict = {"Adria accreted": (0.8, 0.7, 0.45, alpha),
|
||||
"Adria autochton": (0.52, 0.32, 0.2, alpha),
|
||||
"Europe accreted": (0.42, 0.67, 0.88, alpha),
|
||||
"Flexural foredeep and graben fill": (0.6, 0.6, 0.6, alpha), # (1.0, 1.0, 220/255., alpha),
|
||||
"Alpine Tethys": (0., 0.65, 0.3, alpha),
|
||||
"Neotethys": (0.5, 0.8, 0.32, alpha)}
|
||||
|
||||
patches_legend = [Patch(color=value, label=key.capitalize()) for key, value in color_dict.items()]
|
||||
|
||||
for record in reader.records():
|
||||
info = record.attributes
|
||||
shape = record.geometry
|
||||
color = color_dict.get(info['tect_unit'])
|
||||
if not color:
|
||||
color = (1.0, 1.0, 1.0, alpha)
|
||||
ax.add_geometries(shape, crs=TRANSFORM, facecolor=color)
|
||||
|
||||
if add_legend:
|
||||
ax.legend(handles=patches_legend, ncol=3, bbox_to_anchor=(0.5, -0.075), loc='center')
|
||||
|
||||
|
||||
def init_cartopy_map(fig=None, draw_mapbound=True, fill_continents=False,
|
||||
continents_color=None, mapbound_color=None, lakes_color=None, clon=None, clat=None):
|
||||
if not fig:
|
||||
fig = plt.figure()
|
||||
#projection = ccrs.LambertConformal(central_longitude=clon, central_latitude=clat)
|
||||
projection = ccrs.PlateCarree(central_longitude=clon)
|
||||
ax = fig.add_subplot(111, projection=projection)
|
||||
|
||||
if fill_continents:
|
||||
ax.add_feature(cartopy.feature.LAND, color=continents_color)
|
||||
ax.add_feature(cartopy.feature.LAKES, color=lakes_color)
|
||||
if draw_mapbound:
|
||||
#ax.add_feature(cartopy.feature.OCEAN, color='w', linewidth=0.1)
|
||||
ax.add_feature(cartopy.feature.BORDERS, linewidth=0.2, color='0.3')
|
||||
ax.add_feature(cartopy.feature.COASTLINE, linewidth=0.3, color='0.3')
|
||||
return ax
|
||||
|
||||
|
||||
def make_map(fig=None, draw_model=False, model_legends=True, draw_faults=False, width=20,
|
||||
height=14, clon=11, clat=46., draw_grid=True, continents='0.8', lakes='0.85', no_content=False,
|
||||
no_fill=True, alpha_model=0.2, faults_color='k', station_file=None):
|
||||
|
||||
ax = init_cartopy_map(fig=fig, draw_mapbound=not no_content, fill_continents=not no_fill,
|
||||
continents_color=continents, lakes_color=lakes, clon=clon, clat=clat)
|
||||
|
||||
if station_file:
|
||||
with open(station_file, 'r') as fid:
|
||||
stations = json.load(fid)
|
||||
|
||||
lons, lats = zip(*[(sta['longitude'], sta['latitude']) for sta in stations.values()])
|
||||
ax.scatter(lons, lats, c='0.3', s=1, transform=ccrs.PlateCarree(), zorder=5, edgecolors='none', alpha=0.5)
|
||||
|
||||
# if draw_topo:
|
||||
# draw_topo_model(basemap)
|
||||
if draw_model:
|
||||
fnin = '/home/marcel/sciebo/AlpArray_home/tectonic_maps_4dmb_2020_09_17/shape_files/tect_units_alcapadi'
|
||||
draw_alcapadi_model(ax, fnin, add_legend=model_legends, alpha=alpha_model)
|
||||
if draw_faults:
|
||||
fnin = '/home/marcel/sciebo/AlpArray_home/tectonic_maps_4dmb_2020_09_17/shape_files/faults_alcapadi'
|
||||
draw_alcapadi_faults(ax, fnin, faults_color)
|
||||
|
||||
# if not no_content:
|
||||
# basemap.drawcountries(color=line_color, linewidth=0.2)
|
||||
# basemap.drawcoastlines(color=line_color, linewidth=0.3)
|
||||
|
||||
if draw_grid:
|
||||
gl = ax.gridlines(crs=TRANSFORM, draw_labels=True,
|
||||
linewidth=0.5, color='gray', alpha=0.5, linestyle=':')
|
||||
# dashes = [3, 6]
|
||||
# parallels = list(np.arange(-90, 90, lgrid))
|
||||
# parallels_small = [item for item in np.arange(-90, 90, sgrid) if not item in parallels]
|
||||
# basemap.drawparallels(parallels_small, dashes=dashes, color=line_color, linewidth=0.1, zorder=7)
|
||||
# basemap.drawparallels(parallels, dashes=[], color=line_color, linewidth=0.2, zorder=7, labels=[1, 1, 0, 0])
|
||||
# meridians = list(np.arange(-180, 180, lgrid))
|
||||
# meridians_small = [item for item in np.arange(-180, 180, sgrid) if not item in meridians]
|
||||
# basemap.drawmeridians(meridians_small, dashes=dashes, color=line_color, linewidth=0.1, zorder=7)
|
||||
# basemap.drawmeridians(meridians, dashes=[], color=line_color, linewidth=0.2, zorder=7, labels=[0, 0, 1, 1])
|
||||
|
||||
ax.set_extent([clon - width/2, clon + width/2, clat - height/2, clat + height/2])
|
||||
|
||||
return ax
|
||||
|
||||
|
||||
def angle_marker(a1, a2, delta=1.):
|
||||
a1 = np.deg2rad(a1)
|
||||
a2 = np.deg2rad(a2)
|
||||
delta = np.deg2rad(delta)
|
||||
x_vals = [np.sin(angle) for angle in np.arange(a1, a2 + delta, delta)]
|
||||
y_vals = [np.cos(angle) for angle in np.arange(a1, a2 + delta, delta)]
|
||||
xy = zip(x_vals, y_vals)
|
||||
#x1 = np.sin(a1)
|
||||
#y1 = np.cos(a1)
|
||||
#x2 = np.sin(a2)
|
||||
#y2 = np.cos(a2)
|
||||
marker = [(0, 0), *xy, (0, 0)]
|
||||
return marker
|
||||
@@ -1,176 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
import glob
|
||||
import numpy as np
|
||||
import os
|
||||
import json
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
from obspy import Catalog, read_events
|
||||
|
||||
from pylot.core.util.dataprocessing import Metadata
|
||||
|
||||
|
||||
def get_metadata(eventdir):
|
||||
metadata_path = os.path.join(eventdir, 'resp')
|
||||
metadata = Metadata(inventory=metadata_path, verbosity=0)
|
||||
return metadata
|
||||
|
||||
|
||||
|
||||
def set_rc_params(textsize=7.):
|
||||
plt.style.use('/home/marcel/solid_earth.mplstyle')
|
||||
#plt.rcParams.update({'font.size': textsize,
|
||||
# 'font.family': 'sans-serif'})
|
||||
|
||||
|
||||
def normed_figure_ratio_width(width_cm, ratio):
|
||||
width_inch = width_cm / 2.54
|
||||
height_inch = width_inch / ratio
|
||||
return width_inch, height_inch
|
||||
|
||||
|
||||
def normed_figure_ratio_height(height_cm, ratio):
|
||||
height_inch = height_cm / 2.54
|
||||
width_inch = height_inch * ratio
|
||||
return width_inch, height_inch
|
||||
|
||||
|
||||
def normed_figure(width_cm=None, ratio=1.777):
|
||||
#assert ((width_cm and not height_cm) or (height_cm and not width_cm)), 'Choose either of width or height!'
|
||||
set_rc_params()
|
||||
if width_cm:
|
||||
fig = plt.figure(figsize=normed_figure_ratio_width(width_cm, ratio))
|
||||
return fig
|
||||
#elif height_cm:
|
||||
# fig = plt.figure(figsize=normed_figure_ratio_height(height_cm, ratio))
|
||||
return
|
||||
|
||||
|
||||
def pol2cart(lat, lon, r):
|
||||
x = r * np.cos(np.deg2rad(lat)) * np.cos(np.deg2rad(lon))
|
||||
y = r * np.cos(np.deg2rad(lat)) * np.sin(np.deg2rad(lon))
|
||||
z = r * np.sin(np.deg2rad(lat))
|
||||
return x, y, z
|
||||
|
||||
|
||||
def cart2pol(x, y, z):
|
||||
r = np.sqrt(x**2 + y**2 + z**2)
|
||||
theta = np.rad2deg(np.arccos(z/r))
|
||||
phi = np.rad2deg(np.arctan2(y, x))
|
||||
lat = 90. - theta
|
||||
lon = phi
|
||||
return lat, lon, r
|
||||
|
||||
|
||||
def pol2cart_vector(lat, lon, north, east, r_comp):
|
||||
if any(val is None for val in [north, east, r_comp]):
|
||||
return None, None, None
|
||||
phi = np.deg2rad(lon)
|
||||
# change north components to common spherical coordinate convention
|
||||
theta = np.deg2rad(90. - lat)
|
||||
north *= -1
|
||||
x = (np.sin(theta) * np.cos(phi) * r_comp +
|
||||
np.cos(theta) * np.cos(phi) * north -
|
||||
np.sin(phi) * east)
|
||||
y = (np.sin(theta) * np.sin(phi) * r_comp +
|
||||
np.cos(theta) * np.sin(phi) * north +
|
||||
np.cos(phi) * east)
|
||||
z = (np.cos(theta) * r_comp -
|
||||
np.sin(theta) * north)
|
||||
return x, y, z
|
||||
|
||||
|
||||
def read_cat_obspy_dmt_database(databasedir, filemask):
|
||||
infiles = glob.glob(os.path.join(databasedir, '*.a', filemask))
|
||||
cat = Catalog()
|
||||
nPicks = 0
|
||||
for index, infile in enumerate(infiles):
|
||||
print(f'Working on: {infile} ({index + 1}/{len(infiles)})')
|
||||
event = read_events(infile)[0]
|
||||
nPicks += len(event.picks)
|
||||
cat += event
|
||||
|
||||
nEvents = len(cat)
|
||||
print('Number of events: {} (filemask: {})'.format(nEvents, filemask))
|
||||
print('Total # picks: {} ({:.2f} per event)'.format(nPicks, float(nPicks)/nEvents))
|
||||
return cat
|
||||
|
||||
|
||||
def get_event(cat, eventid):
|
||||
for event in cat.events:
|
||||
if event.resource_id.id.split('/')[-1] == eventid:
|
||||
return event
|
||||
|
||||
|
||||
def get_pick4station(picks, network_code, station_code, method='auto'):
|
||||
for pick in picks:
|
||||
if pick.waveform_id.network_code == network_code:
|
||||
if pick.waveform_id.station_code == station_code:
|
||||
if pick.method_id.id.endswith(method):
|
||||
return pick
|
||||
|
||||
|
||||
def delete_picks(picks, nwst_ids_delete):
|
||||
''' Delete picks from list in picks containing ObsPy pick objects'''
|
||||
for index, pick in list(reversed(list(enumerate(picks)))):
|
||||
seed_id = pick.waveform_id.get_seed_string()
|
||||
network, station = seed_id.split('.')[:2]
|
||||
nwst_id = '{}.{}'.format(network, station)
|
||||
if nwst_id in nwst_ids_delete:
|
||||
picks.pop(index)
|
||||
print('Removed pick: ', nwst_id)
|
||||
return picks
|
||||
|
||||
|
||||
def save_all_station_coordinates_dmt_database(dmt_database, fn_out):
|
||||
'''
|
||||
Get all station coordinates from dmt_database and write them (unique) to json outputfile
|
||||
:param dmt_database:
|
||||
:param fn_out:
|
||||
:return:
|
||||
'''
|
||||
stations_dict = {}
|
||||
eventdirs = glob.glob(os.path.join(dmt_database, '*.?'))
|
||||
nEvents = len(eventdirs)
|
||||
for index, eventdir in enumerate(eventdirs):
|
||||
print('Working on event {} ({}/{})'.format(eventdir, index+1, nEvents))
|
||||
metadata = get_metadata(eventdir)
|
||||
current_stations_dict = metadata.get_all_coordinates()
|
||||
for nwst_id, coords in current_stations_dict.items():
|
||||
if not nwst_id in stations_dict.keys():
|
||||
stations_dict[nwst_id] = coords
|
||||
|
||||
with open(fn_out, 'w') as outfile:
|
||||
json.dump(stations_dict, outfile)
|
||||
|
||||
|
||||
def get_metadata(eventdir):
|
||||
metadata_path = os.path.join(eventdir, 'resp')
|
||||
metadata = Metadata(inventory=metadata_path, verbosity=0)
|
||||
return metadata
|
||||
|
||||
|
||||
def get_coordinate_from_dist_baz(station_tmp, dist, baz, mode='deg'):
|
||||
''' function copied from Andre'''
|
||||
# station_tmp: [lon, lat]
|
||||
if mode!='deg' and mode!='rad':
|
||||
print('mode hast to be ether deg or rad!')
|
||||
return None
|
||||
else:
|
||||
station = np.deg2rad(station_tmp)
|
||||
epi_tmp=[0.,0.]
|
||||
if mode=='deg':
|
||||
dist = np.deg2rad(dist)
|
||||
baz = np.deg2rad(baz)
|
||||
az = baz - np.pi
|
||||
if az < 0:
|
||||
az += 2 * np.pi
|
||||
epi_tmp[1] = np.arcsin(np.sin(station[1]) * np.cos(dist) - np.cos(station[1]) * np.sin(dist) * np.cos(az))
|
||||
if (np.cos(dist) - np.sin(station[1]) * np.sin(epi_tmp[1])) / (np.cos(station[1]) * np.cos(epi_tmp[1])) >= 0.:
|
||||
epi_tmp[0] = station[0] - np.arcsin(np.sin(dist) * np.sin(az) / np.cos(epi_tmp[1]))
|
||||
else:
|
||||
epi_tmp[0] = station[0] - np.pi + np.arcsin(np.sin(dist) * np.sin(az) / np.cos(epi_tmp[1]))
|
||||
epi=np.rad2deg(epi_tmp)
|
||||
return epi
|
||||
+9
-7
@@ -1,7 +1,9 @@
|
||||
Cartopy==0.23.0
|
||||
joblib==1.4.2
|
||||
obspy==1.4.1
|
||||
pyaml==24.7.0
|
||||
pyqtgraph==0.13.7
|
||||
PySide2==5.15.8
|
||||
pytest==8.3.2
|
||||
# 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
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,99 +0,0 @@
|
||||
%This is a parameter input file for PyLoT/autoPyLoT.
|
||||
%All main and special settings regarding data handling
|
||||
%and picking are to be set here!
|
||||
%Parameters are optimized for %extent data sets!
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
#main settings#
|
||||
dmt_database_test #datapath# %data path
|
||||
20171010_063224.a #eventID# %event ID for single event processing (* for all events found in database)
|
||||
#invdir# %full path to inventory or dataless-seed file
|
||||
PILOT #datastructure# %choose data structure
|
||||
True #apverbose# %choose 'True' or 'False' for terminal output
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
#NLLoc settings#
|
||||
None #nllocbin# %path to NLLoc executable
|
||||
None #nllocroot# %root of NLLoc-processing directory
|
||||
None #phasefile# %name of autoPyLoT-output phase file for NLLoc
|
||||
None #ctrfile# %name of autoPyLoT-output control file for NLLoc
|
||||
ttime #ttpatter# %pattern of NLLoc ttimes from grid
|
||||
AUTOLOC_nlloc #outpatter# %pattern of NLLoc-output file
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
#parameters for seismic moment estimation#
|
||||
3530.0 #vp# %average P-wave velocity
|
||||
2500.0 #rho# %average rock density [kg/m^3]
|
||||
300.0 0.8 #Qp# %quality factor for P waves (Qp*f^a); list(Qp, a)
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
#settings local magnitude#
|
||||
1.0 1.0 1.0 #WAscaling# %Scaling relation (log(Ao)+Alog(r)+Br+C) of Wood-Anderson amplitude Ao [nm] If zeros are set, original Richter magnitude is calculated!
|
||||
1.0 1.0 #magscaling# %Scaling relation for derived local magnitude [a*Ml+b]. If zeros are set, no scaling of network magnitude is applied!
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
#filter settings#
|
||||
0.03 0.03 #minfreq# %Lower filter frequency [P, S]
|
||||
0.5 0.5 #maxfreq# %Upper filter frequency [P, S]
|
||||
4 4 #filter_order# %filter order [P, S]
|
||||
bandpass bandpass #filter_type# %filter type (bandpass, bandstop, lowpass, highpass) [P, S]
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
#common settings picker#
|
||||
global #extent# %extent of array ("local", "regional" or "global")
|
||||
-100.0 #pstart# %start time [s] for calculating CF for P-picking (if TauPy: seconds relative to estimated onset)
|
||||
50.0 #pstop# %end time [s] for calculating CF for P-picking (if TauPy: seconds relative to estimated onset)
|
||||
-50.0 #sstart# %start time [s] relative to P-onset for calculating CF for S-picking
|
||||
50.0 #sstop# %end time [s] after P-onset for calculating CF for S-picking
|
||||
True #use_taup# %use estimated traveltimes from TauPy for calculating windows for CF
|
||||
ak135 #taup_model# %Define TauPy model for traveltime estimation. Possible values: 1066a, 1066b, ak135, ak135f, herrin, iasp91, jb, prem, pwdk, sp6
|
||||
P,Pdiff,S,SKS #taup_phases# %Specify possible phases for TauPy (comma separated). See Obspy TauPy documentation for possible values.
|
||||
0.03 0.5 #bpz1# %lower/upper corner freq. of first band pass filter Z-comp. [Hz]
|
||||
0.01 0.5 #bpz2# %lower/upper corner freq. of second band pass filter Z-comp. [Hz]
|
||||
0.03 0.5 #bph1# %lower/upper corner freq. of first band pass filter H-comp. [Hz]
|
||||
0.01 0.5 #bph2# %lower/upper corner freq. of second band pass filter z-comp. [Hz]
|
||||
#special settings for calculating CF#
|
||||
%!!Edit the following only if you know what you are doing!!%
|
||||
#Z-component#
|
||||
HOS #algoP# %choose algorithm for P-onset determination (HOS, ARZ, or AR3)
|
||||
300.0 #tlta# %for HOS-/AR-AIC-picker, length of LTA window [s]
|
||||
4 #hosorder# %for HOS-picker, order of Higher Order Statistics
|
||||
2 #Parorder# %for AR-picker, order of AR process of Z-component
|
||||
16.0 #tdet1z# %for AR-picker, length of AR determination window [s] for Z-component, 1st pick
|
||||
10.0 #tpred1z# %for AR-picker, length of AR prediction window [s] for Z-component, 1st pick
|
||||
12.0 #tdet2z# %for AR-picker, length of AR determination window [s] for Z-component, 2nd pick
|
||||
6.0 #tpred2z# %for AR-picker, length of AR prediction window [s] for Z-component, 2nd pick
|
||||
0.001 #addnoise# %add noise to seismogram for stable AR prediction
|
||||
60.0 5.0 20.0 12.0 #tsnrz# %for HOS/AR, window lengths for SNR-and slope estimation [tnoise, tsafetey, tsignal, tslope] [s]
|
||||
50.0 #pickwinP# %for initial AIC pick, length of P-pick window [s]
|
||||
30.0 #Precalcwin# %for HOS/AR, window length [s] for recalculation of CF (relative to 1st pick)
|
||||
2.0 #aictsmooth# %for HOS/AR, take average of samples for smoothing of AIC-function [s]
|
||||
2.0 #tsmoothP# %for HOS/AR, take average of samples in this time window for smoothing CF [s]
|
||||
0.006 #ausP# %for HOS/AR, artificial uplift of samples (aus) of CF (P)
|
||||
2.0 #nfacP# %for HOS/AR, noise factor for noise level determination (P)
|
||||
#H-components#
|
||||
ARH #algoS# %choose algorithm for S-onset determination (ARH or AR3)
|
||||
12.0 #tdet1h# %for HOS/AR, length of AR-determination window [s], H-components, 1st pick
|
||||
6.0 #tpred1h# %for HOS/AR, length of AR-prediction window [s], H-components, 1st pick
|
||||
8.0 #tdet2h# %for HOS/AR, length of AR-determinaton window [s], H-components, 2nd pick
|
||||
4.0 #tpred2h# %for HOS/AR, length of AR-prediction window [s], H-components, 2nd pick
|
||||
4 #Sarorder# %for AR-picker, order of AR process of H-components
|
||||
100.0 #Srecalcwin# %for AR-picker, window length [s] for recalculation of CF (2nd pick) (H)
|
||||
195.0 #pickwinS# %for initial AIC pick, length of S-pick window [s]
|
||||
60.0 10.0 30.0 12.0 #tsnrh# %for ARH/AR3, window lengths for SNR-and slope estimation [tnoise, tsafetey, tsignal, tslope] [s]
|
||||
22.0 #aictsmoothS# %for AIC-picker, take average of samples in this time window for smoothing of AIC-function [s]
|
||||
20.0 #tsmoothS# %for AR-picker, take average of samples for smoothing CF [s] (S)
|
||||
0.001 #ausS# %for HOS/AR, artificial uplift of samples (aus) of CF (S)
|
||||
2.0 #nfacS# %for AR-picker, noise factor for noise level determination (S)
|
||||
#first-motion picker#
|
||||
1 #minfmweight# %minimum required P weight for first-motion determination
|
||||
3.0 #minFMSNR# %miniumum required SNR for first-motion determination
|
||||
10.0 #fmpickwin# %pick window [s] around P onset for calculating zero crossings
|
||||
#quality assessment#
|
||||
0.1 0.2 0.4 0.8 #timeerrorsP# %discrete time errors [s] corresponding to picking weights [0 1 2 3] for P
|
||||
4.0 8.0 16.0 32.0 #timeerrorsS# %discrete time errors [s] corresponding to picking weights [0 1 2 3] for S
|
||||
0.005 #minAICPslope# %below this slope [counts/s] the initial P pick is rejected
|
||||
1.1 #minAICPSNR# %below this SNR the initial P pick is rejected
|
||||
0.002 #minAICSslope# %below this slope [counts/s] the initial S pick is rejected
|
||||
1.3 #minAICSSNR# %below this SNR the initial S pick is rejected
|
||||
20.0 #minsiglength# %length of signal part for which amplitudes must exceed noiselevel [s]
|
||||
1.0 #noisefactor# %noiselevel*noisefactor=threshold
|
||||
10.0 #minpercent# %required percentage of amplitudes exceeding threshold
|
||||
0.1 #zfac# %P-amplitude must exceed at least zfac times RMS-S amplitude
|
||||
100.0 #mdttolerance# %maximum allowed deviation of P picks from median [s]
|
||||
50.0 #wdttolerance# %maximum allowed deviation from Wadati-diagram
|
||||
25.0 #jackfactor# %pick is removed if the variance of the subgroup with the pick removed is larger than the mean variance of all subgroups times safety factor
|
||||
@@ -1,67 +0,0 @@
|
||||
import os
|
||||
import pytest
|
||||
|
||||
from obspy import read_events
|
||||
|
||||
from autoPyLoT import autoPyLoT
|
||||
|
||||
|
||||
class TestAutopickerGlobal():
|
||||
def init(self):
|
||||
self.params_infile = 'pylot_alparray_mantle_corr_stack_0.03-0.5.in'
|
||||
self.test_event_dir = 'dmt_database_test'
|
||||
self.fname_outfile_xml = os.path.join(
|
||||
self.test_event_dir, '20171010_063224.a', 'PyLoT_20171010_063224.a_autopylot.xml'
|
||||
)
|
||||
|
||||
# check if the input files exist
|
||||
if not os.path.isfile(self.params_infile):
|
||||
print(f'Test input file {os.path.abspath(self.params_infile)} not found.')
|
||||
return False
|
||||
|
||||
if not os.path.exists(self.test_event_dir):
|
||||
print(
|
||||
f'Test event directory not found at location "{os.path.abspath(self.test_event_dir)}". '
|
||||
f'Make sure to load it from the website first.'
|
||||
)
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def test_autopicker(self):
|
||||
assert self.init(), 'Initialization failed due to missing input files.'
|
||||
# check for output file in test directory and remove it if necessary
|
||||
if os.path.isfile(self.fname_outfile_xml):
|
||||
os.remove(self.fname_outfile_xml)
|
||||
autoPyLoT(inputfile=self.params_infile, eventid='20171010_063224.a', obspyDMT_wfpath='processed')
|
||||
|
||||
# test for different known output files if they are identical or not
|
||||
compare_pickfiles(self.fname_outfile_xml, 'PyLoT_20171010_063224.a_autopylot.xml', True)
|
||||
compare_pickfiles(self.fname_outfile_xml, 'PyLoT_20171010_063224.a_saved_from_GUI.xml', True)
|
||||
compare_pickfiles(self.fname_outfile_xml, 'PyLoT_20171010_063224.a_corrected_taup_times_0.03-0.5_P.xml', False)
|
||||
|
||||
|
||||
def compare_pickfiles(pickfile1: str, pickfile2: str, samefile: bool = True) -> None:
|
||||
"""
|
||||
Compare the pick times and errors from two pick files.
|
||||
|
||||
Parameters:
|
||||
pickfile1 (str): The path to the first pick file.
|
||||
pickfile2 (str): The path to the second pick file.
|
||||
samefile (bool): A flag indicating whether the two files are expected to be the same. Defaults to True.
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
cat1 = read_events(pickfile1)
|
||||
cat2 = read_events(pickfile2)
|
||||
picks1 = sorted(cat1[0].picks, key=lambda pick: str(pick.waveform_id))
|
||||
picks2 = sorted(cat2[0].picks, key=lambda pick: str(pick.waveform_id))
|
||||
pick_times1 = [pick.time for pick in picks1]
|
||||
pick_times2 = [pick.time for pick in picks2]
|
||||
pick_terrs1 = [pick.time_errors for pick in picks1]
|
||||
pick_terrs2 = [pick.time_errors for pick in picks2]
|
||||
|
||||
# check if times and errors are identical or not depending on the samefile flag
|
||||
assert (pick_times1 == pick_times2) is samefile, 'Pick times error'
|
||||
assert (pick_terrs1 == pick_terrs2) is samefile, 'Pick time errors errors'
|
||||
@@ -4,8 +4,10 @@
|
||||
%Parameters are optimized for %extent data sets!
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
#main settings#
|
||||
/home/darius/alparray/waveforms_used #datapath# %data path
|
||||
e0093.173.16 #eventID# %event ID for single event processing (* for all events found in datapath)
|
||||
/home/darius #rootpath# %project path
|
||||
alparray #datapath# %data path
|
||||
waveforms_used #database# %name of data base
|
||||
e0093.173.16 #eventID# %event ID for single event processing (* for all events found in database)
|
||||
/home/darius/alparray/metadata #invdir# %full path to inventory or dataless-seed file
|
||||
PILOT #datastructure# %choose data structure
|
||||
True #apverbose# %choose 'True' or 'False' for terminal output
|
||||
@@ -41,7 +43,6 @@ global #extent# %extent of a
|
||||
875.0 #sstop# %end time [s] after P-onset for calculating CF for S-picking
|
||||
False #use_taup# %use estimated traveltimes from TauPy for calculating windows for CF
|
||||
IASP91 #taup_model# %define TauPy model for traveltime estimation. Possible values: 1066a, 1066b, ak135, ak135f, herrin, iasp91, jb, prem, pwdk, sp6
|
||||
P,Pdiff,S,Sdiff #taup_phases# %Specify possible phases for TauPy (comma separated). See Obspy TauPy documentation for possible values.
|
||||
0.01 0.1 #bpz1# %lower/upper corner freq. of first band pass filter Z-comp. [Hz]
|
||||
0.001 0.5 #bpz2# %lower/upper corner freq. of second band pass filter Z-comp. [Hz]
|
||||
0.01 0.5 #bph1# %lower/upper corner freq. of first band pass filter H-comp. [Hz]
|
||||
|
||||
@@ -4,8 +4,10 @@
|
||||
%Parameters are optimized for %extent data sets!
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
#main settings#
|
||||
/home/darius/alparray/waveforms_used #datapath# %data path
|
||||
e0093.173.16 #eventID# %event ID for single event processing (* for all events found in datapath)
|
||||
/home/darius #rootpath# %project path
|
||||
alparray #datapath# %data path
|
||||
waveforms_used #database# %name of data base
|
||||
e0093.173.16 #eventID# %event ID for single event processing (* for all events found in database)
|
||||
/home/darius/alparray/metadata #invdir# %full path to inventory or dataless-seed file
|
||||
PILOT #datastructure# %choose data structure
|
||||
True #apverbose# %choose 'True' or 'False' for terminal output
|
||||
@@ -41,7 +43,6 @@ global #extent# %extent of a
|
||||
875.0 #sstop# %end time [s] after P-onset for calculating CF for S-picking
|
||||
True #use_taup# %use estimated traveltimes from TauPy for calculating windows for CF
|
||||
IASP91 #taup_model# %define TauPy model for traveltime estimation. Possible values: 1066a, 1066b, ak135, ak135f, herrin, iasp91, jb, prem, pwdk, sp6
|
||||
P,Pdiff,S,Sdiff #taup_phases# %Specify possible phases for TauPy (comma separated). See Obspy TauPy documentation for possible values.
|
||||
0.01 0.1 #bpz1# %lower/upper corner freq. of first band pass filter Z-comp. [Hz]
|
||||
0.001 0.5 #bpz2# %lower/upper corner freq. of second band pass filter Z-comp. [Hz]
|
||||
0.01 0.5 #bph1# %lower/upper corner freq. of first band pass filter H-comp. [Hz]
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import os
|
||||
import sys
|
||||
import unittest
|
||||
import pytest
|
||||
|
||||
import obspy
|
||||
from obspy import UTCDateTime
|
||||
@@ -106,6 +105,7 @@ class TestAutopickStation(unittest.TestCase):
|
||||
# show complete diff when difference in results dictionaries are found
|
||||
self.maxDiff = None
|
||||
|
||||
# @skip("Works")
|
||||
def test_autopickstation_taupy_disabled_gra1(self):
|
||||
expected = {
|
||||
'P': {'picker': 'auto', 'snrdb': 15.405649120980094, 'weight': 0, 'Mo': None, 'marked': [], 'Mw': None,
|
||||
@@ -121,8 +121,8 @@ class TestAutopickStation(unittest.TestCase):
|
||||
with HidePrints():
|
||||
result, station = autopickstation(wfstream=self.gra1, pickparam=self.pickparam_taupy_disabled,
|
||||
metadata=(None, None))
|
||||
compare_dicts(expected=expected['P'], result=result['P'], hint='P-')
|
||||
compare_dicts(expected=expected['S'], result=result['S'], hint='S-')
|
||||
self.assertDictContainsSubset(expected=expected['P'], actual=result['P'])
|
||||
self.assertDictContainsSubset(expected=expected['S'], actual=result['S'])
|
||||
self.assertEqual('GRA1', station)
|
||||
|
||||
def test_autopickstation_taupy_enabled_gra1(self):
|
||||
@@ -140,8 +140,8 @@ class TestAutopickStation(unittest.TestCase):
|
||||
with HidePrints():
|
||||
result, station = autopickstation(wfstream=self.gra1, pickparam=self.pickparam_taupy_enabled,
|
||||
metadata=self.metadata, origin=self.origin)
|
||||
compare_dicts(expected=expected['P'], result=result['P'], hint='P-')
|
||||
compare_dicts(expected=expected['S'], result=result['S'], hint='S-')
|
||||
self.assertDictContainsSubset(expected=expected['P'], actual=result['P'])
|
||||
self.assertDictContainsSubset(expected=expected['S'], actual=result['S'])
|
||||
self.assertEqual('GRA1', station)
|
||||
|
||||
def test_autopickstation_taupy_disabled_gra2(self):
|
||||
@@ -157,8 +157,8 @@ class TestAutopickStation(unittest.TestCase):
|
||||
with HidePrints():
|
||||
result, station = autopickstation(wfstream=self.gra2, pickparam=self.pickparam_taupy_disabled,
|
||||
metadata=(None, None))
|
||||
compare_dicts(expected=expected['P'], result=result['P'], hint='P-')
|
||||
compare_dicts(expected=expected['S'], result=result['S'], hint='S-')
|
||||
self.assertDictContainsSubset(expected=expected['P'], actual=result['P'])
|
||||
self.assertDictContainsSubset(expected=expected['S'], actual=result['S'])
|
||||
self.assertEqual('GRA2', station)
|
||||
|
||||
def test_autopickstation_taupy_enabled_gra2(self):
|
||||
@@ -175,8 +175,8 @@ class TestAutopickStation(unittest.TestCase):
|
||||
with HidePrints():
|
||||
result, station = autopickstation(wfstream=self.gra2, pickparam=self.pickparam_taupy_enabled,
|
||||
metadata=self.metadata, origin=self.origin)
|
||||
compare_dicts(expected=expected['P'], result=result['P'], hint='P-')
|
||||
compare_dicts(expected=expected['S'], result=result['S'], hint='S-')
|
||||
self.assertDictContainsSubset(expected=expected['P'], actual=result['P'])
|
||||
self.assertDictContainsSubset(expected=expected['S'], actual=result['S'])
|
||||
self.assertEqual('GRA2', station)
|
||||
|
||||
def test_autopickstation_taupy_disabled_ech(self):
|
||||
@@ -190,8 +190,8 @@ class TestAutopickStation(unittest.TestCase):
|
||||
'fm': None, 'spe': None, 'channel': u'LHE'}}
|
||||
with HidePrints():
|
||||
result, station = autopickstation(wfstream=self.ech, pickparam=self.pickparam_taupy_disabled)
|
||||
compare_dicts(expected=expected['P'], result=result['P'], hint='P-')
|
||||
compare_dicts(expected=expected['S'], result=result['S'], hint='S-')
|
||||
self.assertDictContainsSubset(expected=expected['P'], actual=result['P'])
|
||||
self.assertDictContainsSubset(expected=expected['S'], actual=result['S'])
|
||||
self.assertEqual('ECH', station)
|
||||
|
||||
def test_autopickstation_taupy_enabled_ech(self):
|
||||
@@ -208,8 +208,8 @@ class TestAutopickStation(unittest.TestCase):
|
||||
with HidePrints():
|
||||
result, station = autopickstation(wfstream=self.ech, pickparam=self.pickparam_taupy_enabled,
|
||||
metadata=self.metadata, origin=self.origin)
|
||||
compare_dicts(expected=expected['P'], result=result['P'], hint='P-')
|
||||
compare_dicts(expected=expected['S'], result=result['S'], hint='S-')
|
||||
self.assertDictContainsSubset(expected=expected['P'], actual=result['P'])
|
||||
self.assertDictContainsSubset(expected=expected['S'], actual=result['S'])
|
||||
self.assertEqual('ECH', station)
|
||||
|
||||
def test_autopickstation_taupy_disabled_fiesa(self):
|
||||
@@ -224,8 +224,8 @@ class TestAutopickStation(unittest.TestCase):
|
||||
'fm': None, 'spe': None, 'channel': u'LHE'}}
|
||||
with HidePrints():
|
||||
result, station = autopickstation(wfstream=self.fiesa, pickparam=self.pickparam_taupy_disabled)
|
||||
compare_dicts(expected=expected['P'], result=result['P'], hint='P-')
|
||||
compare_dicts(expected=expected['S'], result=result['S'], hint='S-')
|
||||
self.assertDictContainsSubset(expected=expected['P'], actual=result['P'])
|
||||
self.assertDictContainsSubset(expected=expected['S'], actual=result['S'])
|
||||
self.assertEqual('FIESA', station)
|
||||
|
||||
def test_autopickstation_taupy_enabled_fiesa(self):
|
||||
@@ -242,8 +242,8 @@ class TestAutopickStation(unittest.TestCase):
|
||||
with HidePrints():
|
||||
result, station = autopickstation(wfstream=self.fiesa, pickparam=self.pickparam_taupy_enabled,
|
||||
metadata=self.metadata, origin=self.origin)
|
||||
compare_dicts(expected=expected['P'], result=result['P'], hint='P-')
|
||||
compare_dicts(expected=expected['S'], result=result['S'], hint='S-')
|
||||
self.assertDictContainsSubset(expected=expected['P'], actual=result['P'])
|
||||
self.assertDictContainsSubset(expected=expected['S'], actual=result['S'])
|
||||
self.assertEqual('FIESA', station)
|
||||
|
||||
def test_autopickstation_gra1_z_comp_missing(self):
|
||||
@@ -272,8 +272,7 @@ class TestAutopickStation(unittest.TestCase):
|
||||
with HidePrints():
|
||||
result, station = autopickstation(wfstream=wfstream, pickparam=self.pickparam_taupy_disabled,
|
||||
metadata=(None, None))
|
||||
compare_dicts(expected=expected['P'], result=result['P'], hint='P-')
|
||||
compare_dicts(expected=expected['S'], result=result['S'], hint='S-')
|
||||
self.assertEqual(expected, result)
|
||||
self.assertEqual('GRA1', station)
|
||||
|
||||
def test_autopickstation_a106_taupy_enabled(self):
|
||||
@@ -291,9 +290,7 @@ class TestAutopickStation(unittest.TestCase):
|
||||
with HidePrints():
|
||||
result, station = autopickstation(wfstream=self.a106, pickparam=self.pickparam_taupy_enabled,
|
||||
metadata=self.metadata, origin=self.origin)
|
||||
compare_dicts(expected=expected['P'], result=result['P'], hint='P-')
|
||||
compare_dicts(expected=expected['S'], result=result['S'], hint='S-')
|
||||
|
||||
self.assertEqual(expected, result)
|
||||
|
||||
def test_autopickstation_station_missing_in_metadata(self):
|
||||
"""This station is not in the metadata, but Taupy is enabled. Taupy should exit cleanly and modify the starttime
|
||||
@@ -314,37 +311,8 @@ class TestAutopickStation(unittest.TestCase):
|
||||
with HidePrints():
|
||||
result, station = autopickstation(wfstream=self.a005a, pickparam=self.pickparam_taupy_enabled,
|
||||
metadata=self.metadata, origin=self.origin)
|
||||
compare_dicts(expected=expected['P'], result=result['P'], hint='P-')
|
||||
compare_dicts(expected=expected['S'], result=result['S'], hint='S-')
|
||||
self.assertEqual(expected, result)
|
||||
|
||||
|
||||
def run_dict_comparison(result, expected):
|
||||
for key, expected_value in expected.items():
|
||||
if isinstance(expected_value, dict):
|
||||
run_dict_comparison(result[key], expected[key])
|
||||
else:
|
||||
res = result[key]
|
||||
if isinstance(res, UTCDateTime) and isinstance(expected_value, UTCDateTime):
|
||||
res = res.timestamp
|
||||
expected_value = expected_value.timestamp
|
||||
assert expected_value == pytest.approx(res), f'{key}: {expected_value} != {res}'
|
||||
|
||||
|
||||
def compare_dicts(result, expected, hint=''):
|
||||
try:
|
||||
run_dict_comparison(result, expected)
|
||||
except AssertionError:
|
||||
raise AssertionError(f'{hint}Dictionaries not equal.'
|
||||
f'\n\n<<Expected>>\n{pretty_print_dict(expected)}'
|
||||
f'\n\n<<Result>>\n{pretty_print_dict(result)}')
|
||||
|
||||
|
||||
def pretty_print_dict(dct):
|
||||
retstr = ''
|
||||
for key, value in sorted(dct.items(), key=lambda x: x[0]):
|
||||
retstr += f"{key} : {value}\n"
|
||||
|
||||
return retstr
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
import pytest
|
||||
from obspy import read, Trace, UTCDateTime
|
||||
|
||||
from pylot.correlation.pick_correlation_correction import XCorrPickCorrection
|
||||
|
||||
|
||||
class TestXCorrPickCorrection():
|
||||
def setup(self):
|
||||
self.make_test_traces()
|
||||
self.make_test_picks()
|
||||
self.t_before = 2.
|
||||
self.t_after = 2.
|
||||
self.cc_maxlag = 0.5
|
||||
|
||||
def make_test_traces(self):
|
||||
# take first trace of test Stream from obspy
|
||||
tr1 = read()[0]
|
||||
# filter trace
|
||||
tr1.filter('bandpass', freqmin=1, freqmax=20)
|
||||
# make a copy and shift the copy by 0.1 s
|
||||
tr2 = tr1.copy()
|
||||
tr2.stats.starttime += 0.1
|
||||
|
||||
self.trace1 = tr1
|
||||
self.trace2 = tr2
|
||||
|
||||
def make_test_picks(self):
|
||||
# create an artificial reference pick on reference trace (trace1) and another one on the 0.1 s shifted trace
|
||||
self.tpick1 = UTCDateTime('2009-08-24T00:20:07.7')
|
||||
# shift the second pick by 0.2 s, the correction should be around 0.1 s now
|
||||
self.tpick2 = self.tpick1 + 0.2
|
||||
|
||||
def test_slice_trace_okay(self):
|
||||
|
||||
self.setup()
|
||||
xcpc = XCorrPickCorrection(UTCDateTime(), Trace(), UTCDateTime(), Trace(),
|
||||
t_before=self.t_before, t_after=self.t_after, cc_maxlag=self.cc_maxlag)
|
||||
|
||||
test_trace = self.trace1
|
||||
pick_time = self.tpick2
|
||||
|
||||
sliced_trace = xcpc.slice_trace(test_trace, pick_time)
|
||||
assert ((sliced_trace.stats.starttime == pick_time - self.t_before - self.cc_maxlag / 2)
|
||||
and (sliced_trace.stats.endtime == pick_time + self.t_after + self.cc_maxlag / 2))
|
||||
|
||||
def test_slice_trace_fails(self):
|
||||
self.setup()
|
||||
|
||||
test_trace = self.trace1
|
||||
pick_time = self.tpick1
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
xcpc = XCorrPickCorrection(UTCDateTime(), Trace(), UTCDateTime(), Trace(),
|
||||
t_before=self.t_before + 20, t_after=self.t_after, cc_maxlag=self.cc_maxlag)
|
||||
xcpc.slice_trace(test_trace, pick_time)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
xcpc = XCorrPickCorrection(UTCDateTime(), Trace(), UTCDateTime(), Trace(),
|
||||
t_before=self.t_before, t_after=self.t_after + 50, cc_maxlag=self.cc_maxlag)
|
||||
xcpc.slice_trace(test_trace, pick_time)
|
||||
|
||||
def test_cross_correlation(self):
|
||||
self.setup()
|
||||
|
||||
# create XCorrPickCorrection object
|
||||
xcpc = XCorrPickCorrection(self.tpick1, self.trace1, self.tpick2, self.trace2, t_before=self.t_before,
|
||||
t_after=self.t_after, cc_maxlag=self.cc_maxlag)
|
||||
|
||||
# execute correlation
|
||||
correction, cc_max, uncert, fwfm = xcpc.cross_correlation(False, '', '')
|
||||
|
||||
# define awaited test result
|
||||
test_result = (-0.09983091718314982, 0.9578431835689154, 0.0015285160561610929, 0.03625786256084631)
|
||||
|
||||
# check results
|
||||
assert pytest.approx(test_result, rel=1e-6) == (correction, cc_max, uncert, fwfm)
|
||||
Reference in New Issue
Block a user