requake 0.5__tar.gz → 0.6__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. {requake-0.5 → requake-0.6}/CHANGELOG.md +17 -0
  2. {requake-0.5 → requake-0.6}/PKG-INFO +11 -10
  3. {requake-0.5 → requake-0.6}/README.md +9 -8
  4. {requake-0.5 → requake-0.6}/requake/_version.py +3 -3
  5. {requake-0.5 → requake-0.6}/requake/catalog/__init__.py +1 -0
  6. {requake-0.5 → requake-0.6}/requake/catalog/catalog.py +4 -1
  7. requake-0.6/requake/catalog/print_catalog.py +53 -0
  8. {requake-0.5 → requake-0.6}/requake/config/configspec.conf +16 -0
  9. requake-0.6/requake/config/generic_printer.py +66 -0
  10. {requake-0.5 → requake-0.6}/requake/config/parse_arguments.py +83 -16
  11. {requake-0.5 → requake-0.6}/requake/config/rq_setup.py +22 -5
  12. {requake-0.5 → requake-0.6}/requake/config/utils.py +60 -2
  13. {requake-0.5 → requake-0.6}/requake/families/__init__.py +1 -0
  14. {requake-0.5 → requake-0.6}/requake/families/build_families.py +2 -55
  15. {requake-0.5 → requake-0.6}/requake/families/families.py +12 -3
  16. requake-0.6/requake/families/pairs.py +146 -0
  17. {requake-0.5 → requake-0.6}/requake/families/print_families.py +27 -49
  18. requake-0.6/requake/families/print_pairs.py +79 -0
  19. {requake-0.5 → requake-0.6}/requake/formulas/__init__.py +2 -0
  20. requake-0.5/requake/formulas/slip.py → requake-0.6/requake/formulas/moment.py +12 -21
  21. requake-0.6/requake/formulas/slip.py +104 -0
  22. {requake-0.5 → requake-0.6}/requake/main.py +9 -3
  23. {requake-0.5 → requake-0.6}/requake/plot/__init__.py +1 -1
  24. requake-0.6/requake/plot/plot_cumulative.py +174 -0
  25. {requake-0.5 → requake-0.6}/requake/plot/plot_families.py +8 -0
  26. {requake-0.5 → requake-0.6}/requake/plot/plot_timespans.py +3 -2
  27. {requake-0.5 → requake-0.6}/requake/plot/plot_utils.py +8 -4
  28. {requake-0.5 → requake-0.6}/requake.egg-info/PKG-INFO +11 -10
  29. {requake-0.5 → requake-0.6}/requake.egg-info/SOURCES.txt +6 -1
  30. {requake-0.5 → requake-0.6}/setup.py +7 -2
  31. requake-0.5/requake/plot/plot_slip.py +0 -119
  32. {requake-0.5 → requake-0.6}/CONTRIBUTORS.txt +0 -0
  33. {requake-0.5 → requake-0.6}/LICENSE.txt +0 -0
  34. {requake-0.5 → requake-0.6}/MANIFEST.in +0 -0
  35. {requake-0.5 → requake-0.6}/requake/__init__.py +0 -0
  36. {requake-0.5 → requake-0.6}/requake/catalog/read_catalog.py +0 -0
  37. {requake-0.5 → requake-0.6}/requake/catalog/read_catalog_from_csv.py +0 -0
  38. {requake-0.5 → requake-0.6}/requake/catalog/read_catalog_from_fdsnws.py +0 -0
  39. {requake-0.5 → requake-0.6}/requake/catalog/read_catalog_from_quakeml.py +0 -0
  40. {requake-0.5 → requake-0.6}/requake/config/__init__.py +0 -0
  41. {requake-0.5 → requake-0.6}/requake/config/config.py +0 -0
  42. {requake-0.5 → requake-0.6}/requake/config/configobj/__init__.py +0 -0
  43. {requake-0.5 → requake-0.6}/requake/config/configobj/_version.py +0 -0
  44. {requake-0.5 → requake-0.6}/requake/config/configobj/validate.py +0 -0
  45. {requake-0.5 → requake-0.6}/requake/families/build_templates.py +0 -0
  46. {requake-0.5 → requake-0.6}/requake/families/flag_family.py +0 -0
  47. {requake-0.5 → requake-0.6}/requake/formulas/conversion.py +0 -0
  48. {requake-0.5 → requake-0.6}/requake/plot/cached_tiler.py +0 -0
  49. {requake-0.5 → requake-0.6}/requake/plot/map_families.py +0 -0
  50. {requake-0.5 → requake-0.6}/requake/plot/map_tiles.py +0 -0
  51. {requake-0.5 → requake-0.6}/requake/plot/plot_pair.py +0 -0
  52. {requake-0.5 → requake-0.6}/requake/scan/__init__.py +0 -0
  53. {requake-0.5 → requake-0.6}/requake/scan/scan_catalog.py +0 -0
  54. {requake-0.5 → requake-0.6}/requake/scan/scan_templates.py +0 -0
  55. {requake-0.5 → requake-0.6}/requake/waveforms/__init__.py +0 -0
  56. {requake-0.5 → requake-0.6}/requake/waveforms/arrivals.py +0 -0
  57. {requake-0.5 → requake-0.6}/requake/waveforms/station_metadata.py +0 -0
  58. {requake-0.5 → requake-0.6}/requake/waveforms/waveforms.py +0 -0
  59. {requake-0.5 → requake-0.6}/requake.egg-info/dependency_links.txt +0 -0
  60. {requake-0.5 → requake-0.6}/requake.egg-info/entry_points.txt +0 -0
  61. {requake-0.5 → requake-0.6}/requake.egg-info/requires.txt +0 -0
  62. {requake-0.5 → requake-0.6}/requake.egg-info/top_level.txt +0 -0
  63. {requake-0.5 → requake-0.6}/setup.cfg +0 -0
  64. {requake-0.5 → requake-0.6}/versioneer.py +0 -0
@@ -2,6 +2,23 @@
2
2
 
3
3
  Copyright (c) 2021-2024 Claudio Satriano <satriano@ipgp.fr>
4
4
 
5
+ ## v0.6 - 2024-05-04
6
+
7
+ Note: you might want to run `requake update_config` to update your config file
8
+ to the latest version.
9
+
10
+ - Verb `plot_slip` renamed to `plot_cumulative`. This new verb has new options
11
+ to plot cumulative slip, cumulative moment, and cumulative number of events,
12
+ and to make the plot logarithmic.
13
+ - New verb `print_catalog`: print the event catalog to screen
14
+ - New verb `print_pairs`: print the event pairs to screen
15
+ - `print_families`: also print minimum and maximum family magnitudes
16
+ - Additional models to convert magnitude to slip. Currently supported models
17
+ are: Nadeau and Johnson (1998), Beeler et al. (2001), Eshelby (1957).
18
+ Model selection is done using the `mag_to_slip_model` config parameter.
19
+ - New verb `update_config`: update an existing config file to the latest
20
+ version
21
+
5
22
  ## v0.5 - 2024-04-23
6
23
 
7
24
  - New config options: `station_metadata_path` and `waveform_data_path` to
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: requake
3
- Version: 0.5
3
+ Version: 0.6
4
4
  Summary: Repeating earthquakes search and analysis
5
5
  Home-page: https://requake.seismicsource.org
6
6
  Author: Claudio Satriano
@@ -33,7 +33,7 @@ Requires-Dist: argcomplete
33
33
  Requires-Dist: tqdm
34
34
  Requires-Dist: tabulate
35
35
 
36
- <img src="imgs/Requake_logo.svg" width="400">
36
+ <img src="https://cdn.jsdelivr.net/gh/SeismicSource/requake@dc9a6e6fd818e57e0c0d74f9c16f2bfc6d4d148c/imgs/Requake_logo.svg" width="400">
37
37
 
38
38
  # Requake
39
39
 
@@ -122,33 +122,34 @@ To get help, use:
122
122
 
123
123
  requake -h
124
124
 
125
- The different running modes are specified as "verbs" (positional arguments).
126
- Currently supported verbs are:
125
+ Different commands are available:
127
126
 
128
127
  sample_config write sample config file to current directory and exit
128
+ update_config update an existing config file to the latest version
129
129
  read_catalog read an event catalog from web services or from a file
130
+ print_catalog print the event catalog to screen
130
131
  scan_catalog scan an existing catalog for earthquake pairs
132
+ print_pairs print pairs to screen
131
133
  plot_pair plot traces for a given event pair
132
134
  build_families build families of repeating earthquakes from a catalog
133
135
  of pairs
134
136
  print_families print families to screen
135
137
  plot_families plot traces for one ore more event families
136
138
  plot_timespans plot family timespans
137
- plot_slip plot cumulative slip for one or more families
139
+ plot_cumulative cumulative plot for one or more families
138
140
  map_families plot families on a map
139
141
  flag_family flag a family of repeating earthquakes as valid or not
140
- valid. Note that all families are valid by default
141
- when first created
142
+ valid.
142
143
  build_templates build waveform templates for one or more event
143
144
  families
144
145
  scan_templates scan a continuous waveform stream using one or more
145
146
  templates
146
147
 
147
- Certain running modes (e.g., `plot_pair`) require further arguments (use, e.g.,
148
+ Certain commands (e.g., `plot_pair`) require further arguments (use, e.g.,
148
149
  `requake plot_pair -h` to get help).
149
150
 
150
- Requake supports command line tab completion for arguments, thanks to
151
- [argcomplete](https://kislyuk.github.io/argcomplete/).
151
+ Requake supports command line tab completion for commands and arguments, thanks
152
+ to [argcomplete](https://kislyuk.github.io/argcomplete/).
152
153
  To enable command line tab completion, add the following line to your `.bashrc`
153
154
  or `.zshrc`:
154
155
 
@@ -87,33 +87,34 @@ To get help, use:
87
87
 
88
88
  requake -h
89
89
 
90
- The different running modes are specified as "verbs" (positional arguments).
91
- Currently supported verbs are:
90
+ Different commands are available:
92
91
 
93
92
  sample_config write sample config file to current directory and exit
93
+ update_config update an existing config file to the latest version
94
94
  read_catalog read an event catalog from web services or from a file
95
+ print_catalog print the event catalog to screen
95
96
  scan_catalog scan an existing catalog for earthquake pairs
97
+ print_pairs print pairs to screen
96
98
  plot_pair plot traces for a given event pair
97
99
  build_families build families of repeating earthquakes from a catalog
98
100
  of pairs
99
101
  print_families print families to screen
100
102
  plot_families plot traces for one ore more event families
101
103
  plot_timespans plot family timespans
102
- plot_slip plot cumulative slip for one or more families
104
+ plot_cumulative cumulative plot for one or more families
103
105
  map_families plot families on a map
104
106
  flag_family flag a family of repeating earthquakes as valid or not
105
- valid. Note that all families are valid by default
106
- when first created
107
+ valid.
107
108
  build_templates build waveform templates for one or more event
108
109
  families
109
110
  scan_templates scan a continuous waveform stream using one or more
110
111
  templates
111
112
 
112
- Certain running modes (e.g., `plot_pair`) require further arguments (use, e.g.,
113
+ Certain commands (e.g., `plot_pair`) require further arguments (use, e.g.,
113
114
  `requake plot_pair -h` to get help).
114
115
 
115
- Requake supports command line tab completion for arguments, thanks to
116
- [argcomplete](https://kislyuk.github.io/argcomplete/).
116
+ Requake supports command line tab completion for commands and arguments, thanks
117
+ to [argcomplete](https://kislyuk.github.io/argcomplete/).
117
118
  To enable command line tab completion, add the following line to your `.bashrc`
118
119
  or `.zshrc`:
119
120
 
@@ -8,11 +8,11 @@ import json
8
8
 
9
9
  version_json = '''
10
10
  {
11
- "date": "2024-04-23T09:52:34+0200",
11
+ "date": "2024-05-04T17:23:20+0200",
12
12
  "dirty": false,
13
13
  "error": null,
14
- "full-revisionid": "5223a069ca428a69bba654b59dc8bc7b8860cbdd",
15
- "version": "0.5"
14
+ "full-revisionid": "dc9a6e6fd818e57e0c0d74f9c16f2bfc6d4d148c",
15
+ "version": "0.6"
16
16
  }
17
17
  ''' # END VERSION_JSON
18
18
 
@@ -10,3 +10,4 @@ Functions and data structures for managing earthquake catalogs.
10
10
  (https://www.gnu.org/licenses/gpl-3.0-standalone.html)
11
11
  """
12
12
  from .read_catalog import read_catalog # noqa
13
+ from .print_catalog import print_catalog # noqa
@@ -264,10 +264,13 @@ def read_stored_catalog(config):
264
264
  try:
265
265
  cat = RequakeCatalog()
266
266
  cat.read(config.scan_catalog_file)
267
+ cat.sort()
268
+ if not cat:
269
+ raise ValueError('Empty catalog')
267
270
  return cat
268
271
  except ValueError as m:
269
272
  raise ValueError(
270
- f'Error reading catalog file {config.scan_catalog_file}'
273
+ f'Error reading catalog file {config.scan_catalog_file}: {m}'
271
274
  ) from m
272
275
  except FileNotFoundError as m:
273
276
  raise FileNotFoundError(
@@ -0,0 +1,53 @@
1
+ # -*- coding: utf8 -*-
2
+ # SPDX-License-Identifier: GPL-3.0-or-later
3
+ """
4
+ Print the event catalog to screen.
5
+
6
+ :copyright:
7
+ 2021-2024 Claudio Satriano <satriano@ipgp.fr>
8
+ :license:
9
+ GNU General Public License v3.0 or later
10
+ (https://www.gnu.org/licenses/gpl-3.0-standalone.html)
11
+ """
12
+ import logging
13
+ from .catalog import read_stored_catalog
14
+ from ..config.generic_printer import generic_printer
15
+ from ..config.rq_setup import rq_exit
16
+ logger = logging.getLogger(__name__.rsplit('.', maxsplit=1)[-1])
17
+
18
+
19
+ def print_catalog(config):
20
+ """
21
+ Print the event catalog to screen.
22
+
23
+ :param config: Configuration object.
24
+ :type config: config.Config
25
+ """
26
+ try:
27
+ catalog = read_stored_catalog(config)
28
+ except (ValueError, FileNotFoundError) as m:
29
+ logger.error(m)
30
+ rq_exit(1)
31
+
32
+ headers_fmt = [
33
+ ('evid', None),
34
+ ('origin time', None),
35
+ ('longitude', '.4f'),
36
+ ('latitude', '.4f'),
37
+ ('depth\n(km)', '.3f'),
38
+ ('mag\ntype', None),
39
+ ('mag', '.1f')
40
+ ]
41
+ rows = [
42
+ [
43
+ ev.evid,
44
+ ev.orig_time,
45
+ ev.lon,
46
+ ev.lat,
47
+ ev.depth,
48
+ ev.mag_type,
49
+ ev.mag
50
+ ]
51
+ for ev in catalog
52
+ ]
53
+ generic_printer(config, rows, headers_fmt)
@@ -128,3 +128,19 @@ distance_from_lat = float(default=None)
128
128
  ## Normalization will produce an average trace which is more representative
129
129
  ## of the family, but will be less effective in reducing noise.
130
130
  normalize_traces_before_averaging = boolean(default=False)
131
+
132
+ #### Post-processing parameters
133
+ ## Estimation of fault slip (in cm) from repetater's magnitude
134
+ ## Choose between:
135
+ ## NJ1998 : Nadeau and Johnson (1998)
136
+ ## B2001 : Beeler et al. (2001)
137
+ ## E1957 : Eshelby (1957), circular crack model
138
+ ## See Uchida (2019, https://doi.org/10.1186/s40645-019-0284-z) for details
139
+ ## on the models.
140
+ mag_to_slip_model = string(default='NJ1998')
141
+ ## Static stress drop value for B2001 and E1957 models (MPa)
142
+ static_stress_drop = float(default=10.0)
143
+ ## Rigidity value for B2001 and E1957 models (GPa)
144
+ rigidity = float(default=30.0)
145
+ ## Strain hardening coefficient for B2001 model (MPa/cm)
146
+ strain_hardening = float(default=0.5)
@@ -0,0 +1,66 @@
1
+ # -*- coding: utf8 -*-
2
+ # SPDX-License-Identifier: GPL-3.0-or-later
3
+ """
4
+ Generic printer functions for Requake.
5
+
6
+ :copyright:
7
+ 2021-2024 Claudio Satriano <satriano@ipgp.fr>
8
+ :license:
9
+ GNU General Public License v3.0 or later
10
+ (https://www.gnu.org/licenses/gpl-3.0-standalone.html)
11
+ """
12
+ import sys
13
+ import os
14
+ import csv
15
+ import contextlib
16
+ import logging
17
+ from tabulate import tabulate
18
+ logger = logging.getLogger(__name__.rsplit('.', maxsplit=1)[-1])
19
+
20
+
21
+ def generic_printer(config, rows, headers_fmt, print_headers=True):
22
+ """
23
+ A generic printer function for Requake.
24
+
25
+ :param config: Configuration object.
26
+ :type config: config.Config
27
+ :param rows: Rows to print.
28
+ :type rows: list of rows
29
+ :param headers_fmt: Headers and format strings.
30
+ :type headers_fmt: list of tuples of str
31
+ """
32
+ headers = [h[0] for h in headers_fmt]
33
+ floatfmt = [h[1] for h in headers_fmt]
34
+ tablefmt = config.args.format
35
+ if tablefmt == 'csv':
36
+ writer = csv.writer(sys.stdout)
37
+ if print_headers:
38
+ # replace newlines with spaces in headers
39
+ headers = [h.replace('\n', ' ') for h in headers]
40
+ writer.writerow(headers)
41
+ elif tablefmt == 'markdown':
42
+ headers = [h.replace('\n', '<br>') for h in headers]
43
+ if tablefmt == 'csv':
44
+ try:
45
+ writer.writerows(rows)
46
+ except BrokenPipeError:
47
+ # Redirect remaining output to devnull to avoid another
48
+ # BrokenPipeError at shutdown
49
+ devnull = os.open(os.devnull, os.O_WRONLY)
50
+ os.dup2(devnull, sys.stdout.fileno())
51
+ else:
52
+ format_dict = {
53
+ 'simple': 'simple',
54
+ 'markdown': 'github'
55
+ }
56
+ tablefmt = format_dict[config.args.format]
57
+ with contextlib.suppress(BrokenPipeError):
58
+ kwargs = {
59
+ 'headers': headers,
60
+ 'floatfmt': floatfmt,
61
+ 'tablefmt': tablefmt
62
+ }
63
+ line = tabulate(rows, **kwargs)
64
+ if not print_headers:
65
+ line = line.rsplit('\n', maxsplit=1)[-1]
66
+ print(line)
@@ -34,6 +34,22 @@ class NewlineHelpFormatter(argparse.HelpFormatter):
34
34
  return lines
35
35
 
36
36
 
37
+ class SubcommandHelpFormatter(argparse.RawDescriptionHelpFormatter):
38
+ """
39
+ Custom help formatter that removes the list of subcommands from the help
40
+ message.
41
+
42
+ See: https://stackoverflow.com/a/13429281/2021880
43
+ """
44
+ def _format_action(self, action):
45
+ parts = super(
46
+ argparse.RawDescriptionHelpFormatter, self
47
+ )._format_action(action)
48
+ if action.nargs == argparse.PARSER:
49
+ parts = '\n'.join(parts.split('\n')[1:])
50
+ return parts
51
+
52
+
37
53
  def parse_arguments(progname='requake'):
38
54
  """
39
55
  Parse command line arguments.
@@ -44,9 +60,14 @@ def parse_arguments(progname='requake'):
44
60
  :rtype: argparse.Namespace
45
61
  """
46
62
  parser = argparse.ArgumentParser(
47
- description=f'{progname}: Repeating earthquakes search and analysis.'
48
- )
49
- subparser = parser.add_subparsers(dest='action')
63
+ description=f'{progname}: Repeating earthquakes search and analysis.',
64
+ epilog='Use "%(prog)s <command> -h" for help on a specific command\n'
65
+ '(example: "%(prog)s read_catalog -h").\n'
66
+ '\nFull documentation at https://requake.readthedocs.io',
67
+ formatter_class=SubcommandHelpFormatter
68
+ )
69
+ subparser = parser.add_subparsers(dest='action', title='commands')
70
+ subparser.metavar = '<command> [options]'
50
71
  parser.add_argument(
51
72
  '-c',
52
73
  '--configfile',
@@ -73,6 +94,16 @@ def parse_arguments(progname='requake'):
73
94
  help='write sample config file to current directory and exit'
74
95
  )
75
96
  # ---
97
+ # --- update_config
98
+ updateconfig = subparser.add_parser(
99
+ 'update_config',
100
+ help='update an existing config file to the latest version'
101
+ )
102
+ updateconfig.add_argument(
103
+ 'config_file', default='requake.conf', nargs='?',
104
+ help='config file to be updated (default: %(default)s)'
105
+ )
106
+ # ---
76
107
  # --- read catalog
77
108
  readcatalog = subparser.add_parser(
78
109
  'read_catalog',
@@ -103,6 +134,22 @@ def parse_arguments(progname='requake'):
103
134
  '-a', '--append', action='store_true',
104
135
  help='append events to existing catalog'
105
136
  )
137
+ # --- printformat
138
+ # a parent parser for the "format" option,
139
+ # used by the "print" subparsers
140
+ printformat = argparse.ArgumentParser(add_help=False)
141
+ printformat.add_argument(
142
+ '-f', '--format', type=str, default='simple',
143
+ choices=['simple', 'markdown', 'csv'],
144
+ help='format for the output table (default: %(default)s)'
145
+ )
146
+ # ---
147
+ # --- print_catalog
148
+ subparser.add_parser(
149
+ 'print_catalog',
150
+ parents=[printformat],
151
+ help='print the event catalog to screen'
152
+ )
106
153
  # ---
107
154
  # --- scan_catalog
108
155
  subparser.add_parser(
@@ -110,6 +157,23 @@ def parse_arguments(progname='requake'):
110
157
  help='scan an existing catalog for earthquake pairs'
111
158
  )
112
159
  # ---
160
+ # --- print_pairs
161
+ printpairs = subparser.add_parser(
162
+ 'print_pairs',
163
+ parents=[printformat],
164
+ help='print pairs to screen'
165
+ )
166
+ printpairs.add_argument(
167
+ '-c', '--cc_min',
168
+ type=float, default=None,
169
+ help='minimum cross-correlation coefficient (default: %(default)s)'
170
+ )
171
+ printpairs.add_argument(
172
+ '-C', '--cc_max',
173
+ type=float, default=None,
174
+ help='maximum cross-correlation coefficient (default: %(default)s)'
175
+ )
176
+ # ---
113
177
  # --- plot_pair
114
178
  plotpair = subparser.add_parser(
115
179
  'plot_pair',
@@ -166,16 +230,11 @@ def parse_arguments(progname='requake'):
166
230
  )
167
231
  # ---
168
232
  # --- print_families
169
- printfamilies = subparser.add_parser(
233
+ subparser.add_parser(
170
234
  'print_families',
171
- parents=[longerthan, minevents, familynumbers],
235
+ parents=[longerthan, minevents, familynumbers, printformat],
172
236
  help='print families to screen'
173
237
  )
174
- printfamilies.add_argument(
175
- '-f', '--format', type=str, default='simple',
176
- choices=['simple', 'markdown', 'csv'],
177
- help='format for the output table (default: %(default)s)'
178
- )
179
238
  # ---
180
239
  # --- plot_families
181
240
  plotfamilies = subparser.add_parser(
@@ -207,11 +266,20 @@ def parse_arguments(progname='requake'):
207
266
  help='quantity to sort families by on y-axis (default: %(default)s)'
208
267
  )
209
268
  # ---
210
- # --- plot_slip
211
- subparser.add_parser(
212
- 'plot_slip',
269
+ # --- plot_cumulative
270
+ plotcumulative = subparser.add_parser(
271
+ 'plot_cumulative',
213
272
  parents=[longerthan, minevents, familynumbers],
214
- help='plot cumulative slip for one or more families'
273
+ help='cumulative plot for one or more families'
274
+ )
275
+ plotcumulative.add_argument(
276
+ '-q', '--quantity', type=str, default='slip',
277
+ choices=['slip', 'moment', 'number'],
278
+ help='cumulative quantity to plot on y-axis (default: %(default)s)'
279
+ )
280
+ plotcumulative.add_argument(
281
+ '-L', '--logscale', action='store_true',
282
+ help='use log scale for y-axis'
215
283
  )
216
284
  # ---
217
285
  # --- map_families
@@ -242,8 +310,7 @@ def parse_arguments(progname='requake'):
242
310
  # --- flag_family
243
311
  flagfamily = subparser.add_parser(
244
312
  'flag_family',
245
- help='flag a family of repeating earthquakes as valid or not valid. '
246
- 'Note that all families are valid by default when first created'
313
+ help='flag a family of repeating earthquakes as valid or not valid'
247
314
  )
248
315
  flagfamily.add_argument('family_number')
249
316
  flagfamily.add_argument(
@@ -25,7 +25,7 @@ from .._version import get_versions
25
25
  from .config import Config
26
26
  from .utils import (
27
27
  parse_configspec, read_config, validate_config, write_sample_config,
28
- write_ok
28
+ update_config_file, write_ok
29
29
  )
30
30
  # pylint: disable=global-statement,import-outside-toplevel
31
31
 
@@ -50,6 +50,20 @@ def _check_library_versions():
50
50
  OBSPY_VERSION_STR = obspy.__version__
51
51
 
52
52
 
53
+ def _make_outdir(outdir):
54
+ """Create the output directory if it doesn't exist."""
55
+ if not os.path.exists(outdir):
56
+ os.makedirs(outdir)
57
+ readme = os.path.join(outdir, 'README.txt')
58
+ if not os.path.exists(readme):
59
+ with open(readme, 'w', encoding='utf8') as fp:
60
+ fp.write('This is the Requake output directory.\n\n')
61
+ fp.write('''\
62
+ Do not manually edit the files in this directory, unless you know what you
63
+ are doing.
64
+ ''')
65
+
66
+
53
67
  def _setup_logging(config, progname, action_name):
54
68
  """Set up the logging infrastructure."""
55
69
  global logger
@@ -68,8 +82,7 @@ def _setup_logging(config, progname, action_name):
68
82
  'build_families'
69
83
  ]
70
84
  if action_name in logging_actions:
71
- if not os.path.exists(config.args.outdir):
72
- os.makedirs(config.args.outdir)
85
+ _make_outdir(config.args.outdir)
73
86
  logfile = os.path.join(
74
87
  config.args.outdir, f'{progname}.{action_name}.log')
75
88
  filehand = logging.FileHandler(filename=logfile, mode='a')
@@ -190,6 +203,9 @@ def configure(args):
190
203
  if args.action == 'sample_config':
191
204
  write_sample_config(configspec, 'requake')
192
205
  sys.exit(0)
206
+ if args.action == 'update_config':
207
+ update_config_file(args.configfile, configspec)
208
+ sys.exit(0)
193
209
  config_obj = read_config(args.configfile, configspec)
194
210
  # Set to None all the 'None' strings
195
211
  for key, value in config_obj.dict().items():
@@ -261,8 +277,9 @@ def rq_exit(retval=0, abort=False, progname='requake'):
261
277
  """Exit as gracefully as possible."""
262
278
  if abort:
263
279
  print('\nAborting.')
264
- logger.debug(f'{progname} ABORTED\n\n')
265
- else:
280
+ if logger is not None:
281
+ logger.debug(f'{progname} ABORTED\n\n')
282
+ elif logger is not None:
266
283
  logger.debug(f'{progname} END\n\n')
267
284
  logging.shutdown()
268
285
  sys.exit(retval)
@@ -12,8 +12,8 @@ Utility functions for Requake.
12
12
  import os
13
13
  import sys
14
14
  import locale
15
- import traceback
16
- from .. import __version__
15
+ import shutil
16
+ from datetime import datetime
17
17
  from .configobj import ConfigObj
18
18
  from .configobj.validate import Validator
19
19
  locale.setlocale(locale.LC_ALL, '')
@@ -133,6 +133,53 @@ def validate_config(config_obj):
133
133
  err_exit('No configuration value present!')
134
134
 
135
135
 
136
+ def update_config_file(config_file, configspec):
137
+ """
138
+ Update a configuration file to the latest version.
139
+
140
+ :param config_file: Configuration file.
141
+ :type config_file: str
142
+ :param configspec: Configuration specification file.
143
+ :type configspec: str
144
+ """
145
+ config_obj = read_config(config_file, configspec)
146
+ val = Validator()
147
+ config_obj.validate(val)
148
+ mod_time = datetime.fromtimestamp(os.path.getmtime(config_file))
149
+ mod_time_str = mod_time.strftime('%Y%m%d_%H%M%S')
150
+ config_file_old = f'{config_file}.{mod_time_str}'
151
+ ans = input(
152
+ f'Ok to update {config_file}? [y/N]\n'
153
+ f'(Old file will be saved as {config_file_old}) '
154
+ )
155
+ if ans not in ['y', 'Y']:
156
+ sys.exit(0)
157
+ config_new = ConfigObj(configspec=configspec, default_encoding='utf8')
158
+ config_new = read_config(None, configspec)
159
+ config_new.validate(val)
160
+ config_new.defaults = []
161
+ config_new.comments = configspec.comments
162
+ config_new.initial_comment = config_obj.initial_comment
163
+ config_new.final_comment = configspec.final_comment
164
+ for k, v in config_obj.items():
165
+ if k not in config_new:
166
+ continue
167
+ # Fix for force_list(default=None)
168
+ if v == ['None', ]:
169
+ v = None
170
+ config_new[k] = v
171
+ migrate_options = {
172
+ # 'old_option': 'new_option'
173
+ }
174
+ for old_opt, new_opt in migrate_options.items():
175
+ if old_opt in config_obj and config_obj[old_opt] != 'None':
176
+ config_new[new_opt] = config_obj[old_opt]
177
+ shutil.copyfile(config_file, config_file_old)
178
+ with open(config_file, 'wb') as fp:
179
+ config_new.write(fp)
180
+ print(f'{config_file}: updated')
181
+
182
+
136
183
  def manage_uncaught_exception(exception):
137
184
  """
138
185
  Manage an uncaught exception.
@@ -140,6 +187,13 @@ def manage_uncaught_exception(exception):
140
187
  :param exception: Exception object.
141
188
  :type exception: Exception
142
189
  """
190
+ # pylint: disable=import-outside-toplevel
191
+ from .. import __version__
192
+ import traceback
193
+ import numpy as np
194
+ import scipy as sp
195
+ import obspy
196
+ import matplotlib
143
197
  sys.stderr.write("""
144
198
  # BEGIN TRACEBACK #############################################################
145
199
  """)
@@ -160,6 +214,10 @@ Include the following information in your report:
160
214
  """)
161
215
  sys.stderr.write(f' Requake version: {__version__}\n')
162
216
  sys.stderr.write(f' Python version: {sys.version}\n')
217
+ sys.stderr.write(f' NumPy version: {np.__version__}\n')
218
+ sys.stderr.write(f' SciPy version: {sp.__version__}\n')
219
+ sys.stderr.write(f' ObsPy version: {obspy.__version__}\n')
220
+ sys.stderr.write(f' Matplotlib version: {matplotlib.__version__}\n')
163
221
  sys.stderr.write(f' Platform: {sys.platform}\n')
164
222
  sys.stderr.write(f' Command line: {" ".join(sys.argv)}\n')
165
223
  sys.stderr.write(f' Error message: {str(exception)}\n')
@@ -10,6 +10,7 @@ earthquakes.
10
10
  GNU General Public License v3.0 or later
11
11
  (https://www.gnu.org/licenses/gpl-3.0-standalone.html)
12
12
  """
13
+ from .print_pairs import print_pairs # noqa
13
14
  from .build_families import build_families # noqa
14
15
  from .print_families import print_families # noqa
15
16
  from .flag_family import flag_family # noqa