requake 0.8__tar.gz → 0.8.1__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 (76) hide show
  1. {requake-0.8 → requake-0.8.1}/CHANGELOG.md +28 -0
  2. {requake-0.8 → requake-0.8.1}/PKG-INFO +2 -2
  3. {requake-0.8 → requake-0.8.1}/requake/_version.py +3 -3
  4. {requake-0.8 → requake-0.8.1}/requake/catalog/catalog.py +2 -3
  5. {requake-0.8 → requake-0.8.1}/requake/catalog/read_catalog.py +5 -5
  6. {requake-0.8 → requake-0.8.1}/requake/config/configspec.conf +8 -0
  7. {requake-0.8 → requake-0.8.1}/requake/config/parse_arguments.py +20 -3
  8. {requake-0.8 → requake-0.8.1}/requake/config/rq_setup.py +1 -1
  9. {requake-0.8 → requake-0.8.1}/requake/database/catalog.py +5 -5
  10. {requake-0.8 → requake-0.8.1}/requake/database/db.py +12 -7
  11. {requake-0.8 → requake-0.8.1}/requake/database/families.py +8 -8
  12. requake-0.8.1/requake/database/pairs.py +481 -0
  13. {requake-0.8 → requake-0.8.1}/requake/database/templates.py +9 -9
  14. requake-0.8.1/requake/database/trace_metadata.py +335 -0
  15. {requake-0.8 → requake-0.8.1}/requake/families/build_families.py +26 -15
  16. {requake-0.8 → requake-0.8.1}/requake/families/families.py +2 -2
  17. {requake-0.8 → requake-0.8.1}/requake/families/flag_family.py +1 -1
  18. {requake-0.8 → requake-0.8.1}/requake/families/pairs.py +8 -17
  19. requake-0.8.1/requake/families/print_families.py +247 -0
  20. {requake-0.8 → requake-0.8.1}/requake/families/print_pairs.py +17 -7
  21. requake-0.8.1/requake/scan/scan_catalog.py +645 -0
  22. {requake-0.8 → requake-0.8.1}/requake/scan/scan_templates.py +3 -3
  23. {requake-0.8 → requake-0.8.1}/requake/waveforms/waveform_pair.py +127 -37
  24. {requake-0.8 → requake-0.8.1}/requake/waveforms/waveforms.py +121 -6
  25. {requake-0.8 → requake-0.8.1}/requake.egg-info/PKG-INFO +2 -2
  26. {requake-0.8 → requake-0.8.1}/requake.egg-info/SOURCES.txt +1 -0
  27. requake-0.8/requake/database/pairs.py +0 -180
  28. requake-0.8/requake/families/print_families.py +0 -118
  29. requake-0.8/requake/scan/scan_catalog.py +0 -132
  30. {requake-0.8 → requake-0.8.1}/CONTRIBUTORS.txt +0 -0
  31. {requake-0.8 → requake-0.8.1}/LICENSE.txt +0 -0
  32. {requake-0.8 → requake-0.8.1}/MANIFEST.in +0 -0
  33. {requake-0.8 → requake-0.8.1}/README.md +0 -0
  34. {requake-0.8 → requake-0.8.1}/pyproject.toml +0 -0
  35. {requake-0.8 → requake-0.8.1}/requake/__init__.py +0 -0
  36. {requake-0.8 → requake-0.8.1}/requake/catalog/__init__.py +0 -0
  37. {requake-0.8 → requake-0.8.1}/requake/catalog/print_catalog.py +0 -0
  38. {requake-0.8 → requake-0.8.1}/requake/catalog/read_catalog_from_csv.py +0 -0
  39. {requake-0.8 → requake-0.8.1}/requake/catalog/read_catalog_from_fdsnws.py +0 -0
  40. {requake-0.8 → requake-0.8.1}/requake/catalog/read_catalog_from_quakeml.py +0 -0
  41. {requake-0.8 → requake-0.8.1}/requake/config/__init__.py +0 -0
  42. {requake-0.8 → requake-0.8.1}/requake/config/config.py +0 -0
  43. {requake-0.8 → requake-0.8.1}/requake/config/configobj/LICENSE +0 -0
  44. {requake-0.8 → requake-0.8.1}/requake/config/configobj/__init__.py +0 -0
  45. {requake-0.8 → requake-0.8.1}/requake/config/configobj/_version.py +0 -0
  46. {requake-0.8 → requake-0.8.1}/requake/config/configobj/validate.py +0 -0
  47. {requake-0.8 → requake-0.8.1}/requake/config/generic_printer.py +0 -0
  48. {requake-0.8 → requake-0.8.1}/requake/config/utils.py +0 -0
  49. {requake-0.8 → requake-0.8.1}/requake/database/__init__.py +0 -0
  50. {requake-0.8 → requake-0.8.1}/requake/families/__init__.py +0 -0
  51. {requake-0.8 → requake-0.8.1}/requake/families/build_templates.py +0 -0
  52. {requake-0.8 → requake-0.8.1}/requake/formulas/__init__.py +0 -0
  53. {requake-0.8 → requake-0.8.1}/requake/formulas/conversion.py +0 -0
  54. {requake-0.8 → requake-0.8.1}/requake/formulas/moment.py +0 -0
  55. {requake-0.8 → requake-0.8.1}/requake/formulas/slip.py +0 -0
  56. {requake-0.8 → requake-0.8.1}/requake/main.py +0 -0
  57. {requake-0.8 → requake-0.8.1}/requake/plot/__init__.py +0 -0
  58. {requake-0.8 → requake-0.8.1}/requake/plot/cached_tiler.py +0 -0
  59. {requake-0.8 → requake-0.8.1}/requake/plot/colormaps.py +0 -0
  60. {requake-0.8 → requake-0.8.1}/requake/plot/map_families.py +0 -0
  61. {requake-0.8 → requake-0.8.1}/requake/plot/map_tiles.py +0 -0
  62. {requake-0.8 → requake-0.8.1}/requake/plot/plot_cumulative.py +0 -0
  63. {requake-0.8 → requake-0.8.1}/requake/plot/plot_families.py +0 -0
  64. {requake-0.8 → requake-0.8.1}/requake/plot/plot_pair.py +0 -0
  65. {requake-0.8 → requake-0.8.1}/requake/plot/plot_timespans.py +0 -0
  66. {requake-0.8 → requake-0.8.1}/requake/plot/plot_utils.py +0 -0
  67. {requake-0.8 → requake-0.8.1}/requake/scan/__init__.py +0 -0
  68. {requake-0.8 → requake-0.8.1}/requake/waveforms/__init__.py +0 -0
  69. {requake-0.8 → requake-0.8.1}/requake/waveforms/arrivals.py +0 -0
  70. {requake-0.8 → requake-0.8.1}/requake/waveforms/station_metadata.py +0 -0
  71. {requake-0.8 → requake-0.8.1}/requake.egg-info/dependency_links.txt +0 -0
  72. {requake-0.8 → requake-0.8.1}/requake.egg-info/entry_points.txt +0 -0
  73. {requake-0.8 → requake-0.8.1}/requake.egg-info/requires.txt +0 -0
  74. {requake-0.8 → requake-0.8.1}/requake.egg-info/top_level.txt +0 -0
  75. {requake-0.8 → requake-0.8.1}/setup.cfg +0 -0
  76. {requake-0.8 → requake-0.8.1}/setup.py +0 -0
@@ -2,6 +2,30 @@
2
2
 
3
3
  Copyright (c) 2021-2026 Claudio Satriano <satriano@ipgp.fr>
4
4
 
5
+ ## [0.8.1] - 2026-05-31
6
+
7
+ - Slimmed-down DB size by optimizing `event_pairs` storage. This allows storing
8
+ also invalid pairs, so that resuming catalog scan is faster.
9
+ - Dramatic speedup of event pairs building by using a KD-tree
10
+ spatial index and approximate geodetic distance calculations.
11
+ - Dramatic speedup of waveform fetching by improving the caching mechanism.
12
+ A new config option `catalog_waveform_cache_size` allows to set the maximum
13
+ number of waveforms to cache.
14
+ - Dramatically faster reading of event pairs with cross-correlation
15
+ filters in large catalogs, thanks to the new SQLite-backed storage.
16
+ - Add an optional disk cache, through the `catalog_waveform_disk_cache_enabled`
17
+ config option.
18
+ - `scan_catalog`: when running as a batch job, log a progress
19
+ report every minute, instead of showing the progress bar.
20
+ - `scan_catalog`: if event pairs already exist in the database,
21
+ ask whether to overwrite or continue from an interrupted run.
22
+ Add `--force` to restart from scratch and `--force-continue`
23
+ (short `-c`) to resume without prompting.
24
+ - `print_families`: add horizontal and vertical distance min/max values
25
+ to both summary and detailed (`-d`) outputs.
26
+ - `print_families`: add `-f stats` output format for summary family
27
+ statistics.
28
+
5
29
  ## [0.8] - 2026-05-22
6
30
 
7
31
  This release introduces a major storage change.
@@ -13,6 +37,9 @@ data access performance, especially for large catalogs.
13
37
  This change is not backward compatible at the data format level. The
14
38
  high-level behavior of the command-line interface remains unchanged.
15
39
 
40
+ Note: further improvements based on this new storage system will be introduced
41
+ throughout the 0.8.x release series.
42
+
16
43
  ## [0.7.3] - 2026-04-14
17
44
 
18
45
  This release is intended to validate the new release workflow.
@@ -151,6 +178,7 @@ Mostly a bugfix release with a slightly improved trace plotting.
151
178
 
152
179
  - Initial release, not yet feature complete
153
180
 
181
+ [0.8.1]: https://github.com/SeismicSource/requake/compare/v0.8...v0.8.1
154
182
  [0.8]: https://github.com/SeismicSource/requake/compare/v0.7.3...v0.8
155
183
  [0.7.3]: https://github.com/SeismicSource/requake/compare/v0.7.2...v0.7.3
156
184
  [0.7.2]: https://github.com/SeismicSource/requake/compare/v0.7.1...v0.7.2
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: requake
3
- Version: 0.8
3
+ Version: 0.8.1
4
4
  Summary: Repeating earthquakes search and analysis
5
5
  Author-email: Claudio Satriano <satriano@ipgp.fr>
6
6
  License-Expression: GPL-3.0-or-later
@@ -37,7 +37,7 @@ Dynamic: description
37
37
  Dynamic: description-content-type
38
38
  Dynamic: license-file
39
39
 
40
- <img src="https://cdn.jsdelivr.net/gh/SeismicSource/requake@e3ac4cce14f782d62a5f22f432a5391a2ccacfe7/imgs/Requake_logo.svg" width="400">
40
+ <img src="https://cdn.jsdelivr.net/gh/SeismicSource/requake@166143d5bcff5dced4ef1b9a3cc7c746b0cc1b63/imgs/Requake_logo.svg" width="400">
41
41
 
42
42
  # Requake
43
43
 
@@ -8,11 +8,11 @@ import json
8
8
 
9
9
  version_json = '''
10
10
  {
11
- "date": "2026-05-22T08:53:09+0200",
11
+ "date": "2026-05-31T15:55:08+0200",
12
12
  "dirty": false,
13
13
  "error": null,
14
- "full-revisionid": "e3ac4cce14f782d62a5f22f432a5391a2ccacfe7",
15
- "version": "0.8"
14
+ "full-revisionid": "166143d5bcff5dced4ef1b9a3cc7c746b0cc1b63",
15
+ "version": "0.8.1"
16
16
  }
17
17
  ''' # END VERSION_JSON
18
18
 
@@ -11,7 +11,6 @@ Classes and functions for downloading, reading and writing catalogs.
11
11
  """
12
12
  import numpy as np
13
13
  from obspy import UTCDateTime
14
- from ..config import config
15
14
  from ..formulas import float_or_none
16
15
  from ..waveforms import get_traceid_coords
17
16
 
@@ -230,7 +229,7 @@ def read_stored_catalog():
230
229
  """
231
230
  from ..database.catalog import read_catalog as read_catalog_from_db
232
231
 
233
- cat = read_catalog_from_db(config)
232
+ cat = read_catalog_from_db()
234
233
  cat.sort()
235
234
  if not cat:
236
235
  raise ValueError('Empty catalog')
@@ -246,7 +245,7 @@ def fix_non_locatable_events(catalog):
246
245
  """
247
246
  if not any(ev.lat is None or ev.lon is None for ev in catalog):
248
247
  return
249
- traceid_coords = get_traceid_coords(config)
248
+ traceid_coords = get_traceid_coords()
250
249
  mean_lat = np.mean([
251
250
  coords['latitude'] for coords in traceid_coords.values()])
252
251
  mean_lon = np.mean([
@@ -117,8 +117,8 @@ def read_catalog():
117
117
  catalog = read_stored_catalog()
118
118
  nevs_read = len(catalog)
119
119
  logger.info(
120
- f'{nevs_read} events read from db file '
121
- f'{get_db_path(config)}'
120
+ f'{nevs_read:n} events read from db file '
121
+ f'{get_db_path()}'
122
122
  )
123
123
  logger.info('Reading catalog...')
124
124
  input_cat_file = config.args.catalog_file
@@ -150,9 +150,9 @@ def read_catalog():
150
150
  # Sort catalog in increasing time order
151
151
  catalog.sort()
152
152
  # Write catalog to the output database
153
- write_stored_catalog(catalog, config)
153
+ write_stored_catalog(catalog)
154
154
  nevs_written = len(catalog) - nevs_read
155
155
  logger.info(
156
- f'{nevs_written} events written to db file '
157
- f'{get_db_path(config)}'
156
+ f'{nevs_written:n} events written to db file '
157
+ f'{get_db_path()}'
158
158
  )
@@ -68,6 +68,14 @@ catalog_search_range = float(default=30)
68
68
  ## If more than one trace_id is specified (separated by commas), the closest
69
69
  ## station will be used for a given event pair.
70
70
  catalog_trace_id = force_list(default=NET.STA.LOC.CHAN)
71
+ ## Maximum number of event waveforms kept in memory cache during
72
+ ## catalog-based scan. Higher values increase memory usage and can reduce
73
+ ## repeated waveform downloads.
74
+ catalog_waveform_cache_size = integer(min=2, default=5000)
75
+ ## Keep a persistent copy of fetched event waveforms on disk to speed up
76
+ ## reruns. This on-disk cache lives in OUTDIR/waveform_cache and is not
77
+ ## limited by catalog_waveform_cache_size.
78
+ catalog_waveform_disk_cache_enabled = boolean(default=true)
71
79
 
72
80
  #### Template-based scan
73
81
  ### The following parameters are for template-based scan:
@@ -160,11 +160,23 @@ def parse_arguments(progname='requake'):
160
160
  )
161
161
  # ---
162
162
  # --- scan_catalog
163
- subparser.add_parser(
163
+ scan_catalog = subparser.add_parser(
164
164
  'scan_catalog',
165
- parents=[force],
166
165
  help='scan an existing catalog for earthquake pairs'
167
166
  )
167
+ scan_catalog_mode = scan_catalog.add_mutually_exclusive_group()
168
+ scan_catalog_mode.add_argument(
169
+ '-f',
170
+ '--force',
171
+ action='store_true',
172
+ help='delete existing event pairs and restart the scan'
173
+ )
174
+ scan_catalog_mode.add_argument(
175
+ '-c',
176
+ '--force-continue',
177
+ action='store_true',
178
+ help='continue an interrupted scan and keep existing event pairs'
179
+ )
168
180
  # ---
169
181
  # --- print_pairs
170
182
  print_pairs = subparser.add_parser(
@@ -324,10 +336,15 @@ def parse_arguments(progname='requake'):
324
336
  print_families = subparser.add_parser(
325
337
  'print_families',
326
338
  parents=[
327
- longerthan, shorterthan, minevents, family_numbers, print_format
339
+ longerthan, shorterthan, minevents, family_numbers
328
340
  ],
329
341
  help='print families to screen'
330
342
  )
343
+ print_families.add_argument(
344
+ '-f', '--format', type=str, default='simple',
345
+ choices=['simple', 'markdown', 'csv', 'stats'],
346
+ help='format for the output (default: %(default)s)'
347
+ )
331
348
  print_families.add_argument(
332
349
  '-d', '--detailed', action='store_true',
333
350
  help='print detailed information for each family, including a list '
@@ -273,7 +273,7 @@ def configure(args):
273
273
  should_write_catalog = (
274
274
  args.action == 'read_catalog'
275
275
  and not args.append
276
- and not write_ok(get_db_path(config), args.force)
276
+ and not write_ok(get_db_path(), args.force)
277
277
  )
278
278
  if should_write_catalog:
279
279
  print('Exiting now.')
@@ -86,9 +86,9 @@ def _event_from_row(row):
86
86
  )
87
87
 
88
88
 
89
- def write_catalog(catalog, config, db_path=None):
89
+ def write_catalog(catalog, db_path=None):
90
90
  """Write the stored catalog into SQLite."""
91
- conn = get_db_connection(config, initdb=True, db_path=db_path)
91
+ conn = get_db_connection(initdb=True, db_path=db_path)
92
92
  try:
93
93
  cursor = conn.cursor()
94
94
  _ensure_catalog_table(cursor)
@@ -112,11 +112,11 @@ def write_catalog(catalog, config, db_path=None):
112
112
  conn.close()
113
113
 
114
114
 
115
- def read_catalog(config, db_path=None):
115
+ def read_catalog(db_path=None):
116
116
  """Read the stored catalog from SQLite."""
117
117
  from ..catalog.catalog import RequakeCatalog
118
118
 
119
- conn = get_db_connection(config, initdb=False, db_path=db_path)
119
+ conn = get_db_connection(initdb=False, db_path=db_path)
120
120
  try:
121
121
  rows = conn.execute(
122
122
  f'SELECT * FROM {CATALOG_TABLE} ORDER BY orig_time'
@@ -125,7 +125,7 @@ def read_catalog(config, db_path=None):
125
125
  if MISSING_CATALOG_TABLE in str(err):
126
126
  raise FileNotFoundError(
127
127
  'Catalog not found in db file '
128
- f'{get_db_path(config, db_path=db_path)}'
128
+ f'{get_db_path(db_path=db_path)}'
129
129
  ) from err
130
130
  raise
131
131
  finally:
@@ -14,6 +14,7 @@ import contextlib
14
14
  import random
15
15
  import sqlite3
16
16
  import time
17
+ from ..config.config import config
17
18
 
18
19
  DB_VERSION = 1
19
20
  DB_FILENAME = 'requake.sqlite'
@@ -29,7 +30,11 @@ class DatabaseBusyError(RuntimeError):
29
30
  """Raised when retries for transient SQLite lock errors are exhausted."""
30
31
 
31
32
 
32
- def get_db_path(config, db_path=None):
33
+ class DatabaseCorruptError(RuntimeError):
34
+ """Raised when the SQLite database file is corrupt or malformed."""
35
+
36
+
37
+ def get_db_path(db_path=None):
33
38
  """Return the SQLite path."""
34
39
  if db_path is not None:
35
40
  return db_path
@@ -61,9 +66,9 @@ def get_db_path(config, db_path=None):
61
66
  return os.path.join(outdir, DB_FILENAME)
62
67
 
63
68
 
64
- def check_db_exists(config, initdb=False, db_path=None):
69
+ def check_db_exists(initdb=False, db_path=None):
65
70
  """Validate the configured database path for read or init mode."""
66
- db_path = get_db_path(config, db_path=db_path)
71
+ db_path = get_db_path(db_path=db_path)
67
72
  if initdb:
68
73
  _ensure_parent_dir(db_path)
69
74
  return db_path
@@ -138,15 +143,15 @@ def execute_with_retry(operation, operation_name):
138
143
  attempt += 1
139
144
 
140
145
 
141
- def initialize_database_if_needed(config, db_path=None):
146
+ def initialize_database_if_needed(db_path=None):
142
147
  """Initialize database schema version and pragmas if missing."""
143
- conn = get_db_connection(config, initdb=True, db_path=db_path)
148
+ conn = get_db_connection(initdb=True, db_path=db_path)
144
149
  conn.close()
145
150
 
146
151
 
147
- def get_db_connection(config, initdb=False, db_path=None):
152
+ def get_db_connection(initdb=False, db_path=None):
148
153
  """Open a SQLite connection for the configured database file."""
149
- db_path = check_db_exists(config, initdb=initdb, db_path=db_path)
154
+ db_path = check_db_exists(initdb=initdb, db_path=db_path)
150
155
  conn = sqlite3.connect(db_path)
151
156
  _configure_connection(conn)
152
157
  conn.row_factory = sqlite3.Row
@@ -64,9 +64,9 @@ def _family_row(family, event):
64
64
  )
65
65
 
66
66
 
67
- def write_families(families, config):
67
+ def write_families(families):
68
68
  """Write catalog-scan families into SQLite."""
69
- conn = get_db_connection(config, initdb=True)
69
+ conn = get_db_connection(initdb=True)
70
70
  try:
71
71
  cursor = conn.cursor()
72
72
  _ensure_families_table(cursor)
@@ -95,12 +95,12 @@ def write_families(families, config):
95
95
  conn.close()
96
96
 
97
97
 
98
- def read_families(config):
98
+ def read_families():
99
99
  """Read catalog-scan families from SQLite."""
100
100
  from ..catalog import RequakeEvent
101
101
  from ..families.families import Family
102
102
 
103
- conn = get_db_connection(config, initdb=False)
103
+ conn = get_db_connection(initdb=False)
104
104
  try:
105
105
  cursor = conn.cursor()
106
106
  try:
@@ -113,7 +113,7 @@ def read_families(config):
113
113
  except sqlite3.OperationalError as err:
114
114
  if MISSING_FAMILIES_TABLE in str(err):
115
115
  raise FileNotFoundError(
116
- f'Families not found in db file {get_db_path(config)}'
116
+ f'Families not found in db file {get_db_path()}'
117
117
  ) from err
118
118
  raise
119
119
  finally:
@@ -147,9 +147,9 @@ def read_families(config):
147
147
  return families
148
148
 
149
149
 
150
- def update_family_valid(config, family_number, is_valid):
150
+ def update_family_valid(family_number, is_valid):
151
151
  """Update the validity flag for all events in one family."""
152
- conn = get_db_connection(config, initdb=False)
152
+ conn = get_db_connection(initdb=False)
153
153
  try:
154
154
  cursor = conn.cursor()
155
155
  try:
@@ -166,7 +166,7 @@ def update_family_valid(config, family_number, is_valid):
166
166
  except sqlite3.OperationalError as err:
167
167
  if MISSING_FAMILIES_TABLE in str(err):
168
168
  raise FileNotFoundError(
169
- f'Families not found in db file {get_db_path(config)}'
169
+ f'Families not found in db file {get_db_path()}'
170
170
  ) from err
171
171
  raise
172
172
  conn.commit()