specmod 0.5.0__tar.gz → 0.7.0__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 (275) hide show
  1. {specmod-0.5.0 → specmod-0.7.0}/.github/workflows/build.yml +2 -0
  2. {specmod-0.5.0 → specmod-0.7.0}/.github/workflows/docs.yml +2 -0
  3. specmod-0.7.0/.github/workflows/pr-title.yml +30 -0
  4. {specmod-0.5.0 → specmod-0.7.0}/.github/workflows/release.yml +2 -0
  5. {specmod-0.5.0 → specmod-0.7.0}/.github/workflows/test.yml +6 -0
  6. specmod-0.7.0/.release-please-manifest.json +3 -0
  7. {specmod-0.5.0 → specmod-0.7.0}/AGENTS.md +7 -0
  8. {specmod-0.5.0 → specmod-0.7.0}/CHANGELOG.md +45 -0
  9. {specmod-0.5.0 → specmod-0.7.0}/PKG-INFO +1 -1
  10. {specmod-0.5.0 → specmod-0.7.0}/docs/REFACTOR_PLAN.md +55 -27
  11. {specmod-0.5.0 → specmod-0.7.0}/docs/_builders/specmod_tutorial.py +75 -13
  12. {specmod-0.5.0 → specmod-0.7.0}/docs/releasing-data.md +7 -0
  13. {specmod-0.5.0 → specmod-0.7.0}/docs/roadmap.md +72 -3
  14. {specmod-0.5.0 → specmod-0.7.0}/docs/upgrading.md +131 -4
  15. {specmod-0.5.0 → specmod-0.7.0}/src/specmod/acquire.py +6 -3
  16. {specmod-0.5.0 → specmod-0.7.0}/src/specmod/cli.py +23 -0
  17. {specmod-0.5.0 → specmod-0.7.0}/src/specmod/config/__init__.py +3 -1
  18. {specmod-0.5.0 → specmod-0.7.0}/src/specmod/config/layers.py +77 -1
  19. {specmod-0.5.0 → specmod-0.7.0}/src/specmod/config/sections.py +25 -6
  20. {specmod-0.5.0 → specmod-0.7.0}/src/specmod/config/serialize.py +9 -2
  21. {specmod-0.5.0 → specmod-0.7.0}/src/specmod/datasets.py +142 -4
  22. {specmod-0.5.0 → specmod-0.7.0}/src/specmod/fitting/spectrum.py +5 -4
  23. {specmod-0.5.0 → specmod-0.7.0}/stubs/obspy/core/utcdatetime.pyi +1 -0
  24. specmod-0.7.0/tests/golden/reference.toml +115 -0
  25. {specmod-0.5.0 → specmod-0.7.0}/tests/test_acquire.py +28 -5
  26. {specmod-0.5.0 → specmod-0.7.0}/tests/test_config.py +21 -5
  27. specmod-0.7.0/tests/test_config_pinning.py +155 -0
  28. {specmod-0.5.0 → specmod-0.7.0}/tests/test_fitting_defaults.py +40 -2
  29. {specmod-0.5.0 → specmod-0.7.0}/tests/test_golden_reference.py +49 -35
  30. {specmod-0.5.0 → specmod-0.7.0}/tests/test_magnitude.py +66 -2
  31. specmod-0.7.0/tests/test_path_names.py +220 -0
  32. {specmod-0.5.0 → specmod-0.7.0}/tests/test_pipeline.py +16 -1
  33. specmod-0.7.0/tests/test_pr_title.py +87 -0
  34. {specmod-0.5.0 → specmod-0.7.0}/tests/test_release_config.py +32 -0
  35. {specmod-0.5.0 → specmod-0.7.0}/tests/test_staged.py +10 -1
  36. specmod-0.7.0/tools/check_pr_title.py +131 -0
  37. {specmod-0.5.0 → specmod-0.7.0}/tools/make_golden.py +30 -19
  38. {specmod-0.5.0 → specmod-0.7.0}/tutorial/specmod-tutorial.ipynb +107 -37
  39. specmod-0.5.0/.release-please-manifest.json +0 -3
  40. {specmod-0.5.0 → specmod-0.7.0}/.git-blame-ignore-revs +0 -0
  41. {specmod-0.5.0 → specmod-0.7.0}/.gitignore +0 -0
  42. {specmod-0.5.0 → specmod-0.7.0}/.pre-commit-config.yaml +0 -0
  43. {specmod-0.5.0 → specmod-0.7.0}/.readthedocs.yaml +0 -0
  44. {specmod-0.5.0 → specmod-0.7.0}/CITATION.cff +0 -0
  45. {specmod-0.5.0 → specmod-0.7.0}/CLAUDE.md +0 -0
  46. {specmod-0.5.0 → specmod-0.7.0}/CONTRIBUTING.md +0 -0
  47. {specmod-0.5.0 → specmod-0.7.0}/LICENSE +0 -0
  48. {specmod-0.5.0 → specmod-0.7.0}/README.md +0 -0
  49. {specmod-0.5.0 → specmod-0.7.0}/datasets/magna_2020.toml +0 -0
  50. {specmod-0.5.0 → specmod-0.7.0}/datasets/pnr_2019.toml +0 -0
  51. {specmod-0.5.0 → specmod-0.7.0}/docs/_builders/_notebook.py +0 -0
  52. {specmod-0.5.0 → specmod-0.7.0}/docs/_builders/choosing_a_transform.py +0 -0
  53. {specmod-0.5.0 → specmod-0.7.0}/docs/_static/academic.css +0 -0
  54. {specmod-0.5.0 → specmod-0.7.0}/docs/_static/fonts/OFL-fira-code.txt +0 -0
  55. {specmod-0.5.0 → specmod-0.7.0}/docs/_static/fonts/OFL-merriweather.txt +0 -0
  56. {specmod-0.5.0 → specmod-0.7.0}/docs/_static/fonts/OFL-open-sans.txt +0 -0
  57. {specmod-0.5.0 → specmod-0.7.0}/docs/_static/fonts/README.md +0 -0
  58. {specmod-0.5.0 → specmod-0.7.0}/docs/_static/fonts/fira-code-400-greek.woff2 +0 -0
  59. {specmod-0.5.0 → specmod-0.7.0}/docs/_static/fonts/fira-code-400-latin-ext.woff2 +0 -0
  60. {specmod-0.5.0 → specmod-0.7.0}/docs/_static/fonts/fira-code-400-latin.woff2 +0 -0
  61. {specmod-0.5.0 → specmod-0.7.0}/docs/_static/fonts/merriweather-latin-ext.woff2 +0 -0
  62. {specmod-0.5.0 → specmod-0.7.0}/docs/_static/fonts/merriweather-latin.woff2 +0 -0
  63. {specmod-0.5.0 → specmod-0.7.0}/docs/_static/fonts/open-sans-greek.woff2 +0 -0
  64. {specmod-0.5.0 → specmod-0.7.0}/docs/_static/fonts/open-sans-latin-ext.woff2 +0 -0
  65. {specmod-0.5.0 → specmod-0.7.0}/docs/_static/fonts/open-sans-latin.woff2 +0 -0
  66. {specmod-0.5.0 → specmod-0.7.0}/docs/_static/fonts.css +0 -0
  67. {specmod-0.5.0 → specmod-0.7.0}/docs/_static/katex/LICENSE +0 -0
  68. {specmod-0.5.0 → specmod-0.7.0}/docs/_static/katex/README.md +0 -0
  69. {specmod-0.5.0 → specmod-0.7.0}/docs/_static/katex/fonts/KaTeX_AMS-Regular.woff2 +0 -0
  70. {specmod-0.5.0 → specmod-0.7.0}/docs/_static/katex/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
  71. {specmod-0.5.0 → specmod-0.7.0}/docs/_static/katex/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
  72. {specmod-0.5.0 → specmod-0.7.0}/docs/_static/katex/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
  73. {specmod-0.5.0 → specmod-0.7.0}/docs/_static/katex/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
  74. {specmod-0.5.0 → specmod-0.7.0}/docs/_static/katex/fonts/KaTeX_Main-Bold.woff2 +0 -0
  75. {specmod-0.5.0 → specmod-0.7.0}/docs/_static/katex/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
  76. {specmod-0.5.0 → specmod-0.7.0}/docs/_static/katex/fonts/KaTeX_Main-Italic.woff2 +0 -0
  77. {specmod-0.5.0 → specmod-0.7.0}/docs/_static/katex/fonts/KaTeX_Main-Regular.woff2 +0 -0
  78. {specmod-0.5.0 → specmod-0.7.0}/docs/_static/katex/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
  79. {specmod-0.5.0 → specmod-0.7.0}/docs/_static/katex/fonts/KaTeX_Math-Italic.woff2 +0 -0
  80. {specmod-0.5.0 → specmod-0.7.0}/docs/_static/katex/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
  81. {specmod-0.5.0 → specmod-0.7.0}/docs/_static/katex/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
  82. {specmod-0.5.0 → specmod-0.7.0}/docs/_static/katex/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
  83. {specmod-0.5.0 → specmod-0.7.0}/docs/_static/katex/fonts/KaTeX_Script-Regular.woff2 +0 -0
  84. {specmod-0.5.0 → specmod-0.7.0}/docs/_static/katex/fonts/KaTeX_Size1-Regular.woff2 +0 -0
  85. {specmod-0.5.0 → specmod-0.7.0}/docs/_static/katex/fonts/KaTeX_Size2-Regular.woff2 +0 -0
  86. {specmod-0.5.0 → specmod-0.7.0}/docs/_static/katex/fonts/KaTeX_Size3-Regular.woff2 +0 -0
  87. {specmod-0.5.0 → specmod-0.7.0}/docs/_static/katex/fonts/KaTeX_Size4-Regular.woff2 +0 -0
  88. {specmod-0.5.0 → specmod-0.7.0}/docs/_static/katex/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
  89. {specmod-0.5.0 → specmod-0.7.0}/docs/_static/katex/katex.min.css +0 -0
  90. {specmod-0.5.0 → specmod-0.7.0}/docs/_static/specmod-logo-academic-dark-mark.svg +0 -0
  91. {specmod-0.5.0 → specmod-0.7.0}/docs/_static/specmod-logo-academic-dark.svg +0 -0
  92. {specmod-0.5.0 → specmod-0.7.0}/docs/_static/specmod-logo-academic-mark.svg +0 -0
  93. {specmod-0.5.0 → specmod-0.7.0}/docs/_static/specmod-logo-academic.svg +0 -0
  94. {specmod-0.5.0 → specmod-0.7.0}/docs/api.md +0 -0
  95. {specmod-0.5.0 → specmod-0.7.0}/docs/branding.md +0 -0
  96. {specmod-0.5.0 → specmod-0.7.0}/docs/choosing-a-transform.md +0 -0
  97. {specmod-0.5.0 → specmod-0.7.0}/docs/conf.py +0 -0
  98. {specmod-0.5.0 → specmod-0.7.0}/docs/contributing.md +0 -0
  99. {specmod-0.5.0 → specmod-0.7.0}/docs/development.md +0 -0
  100. {specmod-0.5.0 → specmod-0.7.0}/docs/documentation.md +0 -0
  101. {specmod-0.5.0 → specmod-0.7.0}/docs/getting-started.md +0 -0
  102. {specmod-0.5.0 → specmod-0.7.0}/docs/guides.md +0 -0
  103. {specmod-0.5.0 → specmod-0.7.0}/docs/index.md +0 -0
  104. {specmod-0.5.0 → specmod-0.7.0}/docs/notebooks/choosing-a-transform.ipynb +0 -0
  105. {specmod-0.5.0 → specmod-0.7.0}/docs/notes/api-audit.md +0 -0
  106. {specmod-0.5.0 → specmod-0.7.0}/docs/notes/window-position.md +0 -0
  107. {specmod-0.5.0 → specmod-0.7.0}/docs/pick-formats.md +0 -0
  108. {specmod-0.5.0 → specmod-0.7.0}/docs/processing.md +0 -0
  109. {specmod-0.5.0 → specmod-0.7.0}/docs/releasing.md +0 -0
  110. {specmod-0.5.0 → specmod-0.7.0}/pyproject.toml +0 -0
  111. {specmod-0.5.0 → specmod-0.7.0}/release-please-config.json +0 -0
  112. {specmod-0.5.0 → specmod-0.7.0}/requirements.txt +0 -0
  113. {specmod-0.5.0 → specmod-0.7.0}/src/specmod/__init__.py +0 -0
  114. {specmod-0.5.0 → specmod-0.7.0}/src/specmod/_vendor/__init__.py +0 -0
  115. {specmod-0.5.0 → specmod-0.7.0}/src/specmod/_vendor/qiinv.py +0 -0
  116. {specmod-0.5.0 → specmod-0.7.0}/src/specmod/api.py +0 -0
  117. {specmod-0.5.0 → specmod-0.7.0}/src/specmod/config/provenance.py +0 -0
  118. {specmod-0.5.0 → specmod-0.7.0}/src/specmod/core/__init__.py +0 -0
  119. {specmod-0.5.0 → specmod-0.7.0}/src/specmod/core/bandwidth.py +0 -0
  120. {specmod-0.5.0 → specmod-0.7.0}/src/specmod/core/collection.py +0 -0
  121. {specmod-0.5.0 → specmod-0.7.0}/src/specmod/core/noise.py +0 -0
  122. {specmod-0.5.0 → specmod-0.7.0}/src/specmod/core/scalogram.py +0 -0
  123. {specmod-0.5.0 → specmod-0.7.0}/src/specmod/core/spectrum.py +0 -0
  124. {specmod-0.5.0 → specmod-0.7.0}/src/specmod/core/units.py +0 -0
  125. {specmod-0.5.0 → specmod-0.7.0}/src/specmod/distance.py +0 -0
  126. {specmod-0.5.0 → specmod-0.7.0}/src/specmod/exceptions.py +0 -0
  127. {specmod-0.5.0 → specmod-0.7.0}/src/specmod/fitting/__init__.py +0 -0
  128. {specmod-0.5.0 → specmod-0.7.0}/src/specmod/fitting/base.py +0 -0
  129. {specmod-0.5.0 → specmod-0.7.0}/src/specmod/fitting/event.py +0 -0
  130. {specmod-0.5.0 → specmod-0.7.0}/src/specmod/fitting/guess.py +0 -0
  131. {specmod-0.5.0 → specmod-0.7.0}/src/specmod/io/__init__.py +0 -0
  132. {specmod-0.5.0 → specmod-0.7.0}/src/specmod/io/backend.py +0 -0
  133. {specmod-0.5.0 → specmod-0.7.0}/src/specmod/io/hdf5.py +0 -0
  134. {specmod-0.5.0 → specmod-0.7.0}/src/specmod/io/layout.py +0 -0
  135. {specmod-0.5.0 → specmod-0.7.0}/src/specmod/magnitude.py +0 -0
  136. {specmod-0.5.0 → specmod-0.7.0}/src/specmod/picks/__init__.py +0 -0
  137. {specmod-0.5.0 → specmod-0.7.0}/src/specmod/picks/base.py +0 -0
  138. {specmod-0.5.0 → specmod-0.7.0}/src/specmod/picks/delimited.py +0 -0
  139. {specmod-0.5.0 → specmod-0.7.0}/src/specmod/picks/events.py +0 -0
  140. {specmod-0.5.0 → specmod-0.7.0}/src/specmod/picks/resolution.py +0 -0
  141. {specmod-0.5.0 → specmod-0.7.0}/src/specmod/picks/snuffler.py +0 -0
  142. {specmod-0.5.0 → specmod-0.7.0}/src/specmod/pipeline.py +0 -0
  143. {specmod-0.5.0 → specmod-0.7.0}/src/specmod/plotting/__init__.py +0 -0
  144. {specmod-0.5.0 → specmod-0.7.0}/src/specmod/plotting/grid.py +0 -0
  145. {specmod-0.5.0 → specmod-0.7.0}/src/specmod/plotting/pair.py +0 -0
  146. {specmod-0.5.0 → specmod-0.7.0}/src/specmod/plotting/style.py +0 -0
  147. {specmod-0.5.0 → specmod-0.7.0}/src/specmod/preprocess.py +0 -0
  148. {specmod-0.5.0 → specmod-0.7.0}/src/specmod/smoothing/__init__.py +0 -0
  149. {specmod-0.5.0 → specmod-0.7.0}/src/specmod/smoothing/base.py +0 -0
  150. {specmod-0.5.0 → specmod-0.7.0}/src/specmod/smoothing/konno_ohmachi.py +0 -0
  151. {specmod-0.5.0 → specmod-0.7.0}/src/specmod/smoothing/log_bins.py +0 -0
  152. {specmod-0.5.0 → specmod-0.7.0}/src/specmod/smoothing/log_window.py +0 -0
  153. {specmod-0.5.0 → specmod-0.7.0}/src/specmod/smoothing/savitzky_golay.py +0 -0
  154. {specmod-0.5.0 → specmod-0.7.0}/src/specmod/sources/__init__.py +0 -0
  155. {specmod-0.5.0 → specmod-0.7.0}/src/specmod/sources/attenuation.py +0 -0
  156. {specmod-0.5.0 → specmod-0.7.0}/src/specmod/sources/composite.py +0 -0
  157. {specmod-0.5.0 → specmod-0.7.0}/src/specmod/sources/motion.py +0 -0
  158. {specmod-0.5.0 → specmod-0.7.0}/src/specmod/sources/source.py +0 -0
  159. {specmod-0.5.0 → specmod-0.7.0}/src/specmod/spreading.py +0 -0
  160. {specmod-0.5.0 → specmod-0.7.0}/src/specmod/staged.py +0 -0
  161. {specmod-0.5.0 → specmod-0.7.0}/src/specmod/tables.py +0 -0
  162. {specmod-0.5.0 → specmod-0.7.0}/src/specmod/transforms/__init__.py +0 -0
  163. {specmod-0.5.0 → specmod-0.7.0}/src/specmod/transforms/base.py +0 -0
  164. {specmod-0.5.0 → specmod-0.7.0}/src/specmod/transforms/cwt.py +0 -0
  165. {specmod-0.5.0 → specmod-0.7.0}/src/specmod/transforms/fft.py +0 -0
  166. {specmod-0.5.0 → specmod-0.7.0}/src/specmod/transforms/multitaper.py +0 -0
  167. {specmod-0.5.0 → specmod-0.7.0}/src/specmod/transforms/prieto.py +0 -0
  168. {specmod-0.5.0 → specmod-0.7.0}/src/specmod/transforms/quadratic.py +0 -0
  169. {specmod-0.5.0 → specmod-0.7.0}/src/specmod/utils.py +0 -0
  170. {specmod-0.5.0 → specmod-0.7.0}/stubs/README.md +0 -0
  171. {specmod-0.5.0 → specmod-0.7.0}/stubs/lmfit/__init__.pyi +0 -0
  172. {specmod-0.5.0 → specmod-0.7.0}/stubs/lmfit/model.pyi +0 -0
  173. {specmod-0.5.0 → specmod-0.7.0}/stubs/lmfit/parameter.pyi +0 -0
  174. {specmod-0.5.0 → specmod-0.7.0}/stubs/obspy/__init__.pyi +0 -0
  175. {specmod-0.5.0 → specmod-0.7.0}/stubs/obspy/clients/__init__.pyi +0 -0
  176. {specmod-0.5.0 → specmod-0.7.0}/stubs/obspy/clients/fdsn/__init__.pyi +0 -0
  177. {specmod-0.5.0 → specmod-0.7.0}/stubs/obspy/core/__init__.pyi +0 -0
  178. {specmod-0.5.0 → specmod-0.7.0}/stubs/obspy/core/event.pyi +0 -0
  179. {specmod-0.5.0 → specmod-0.7.0}/stubs/obspy/core/inventory.pyi +0 -0
  180. {specmod-0.5.0 → specmod-0.7.0}/stubs/obspy/core/stream.pyi +0 -0
  181. {specmod-0.5.0 → specmod-0.7.0}/stubs/obspy/core/trace.pyi +0 -0
  182. {specmod-0.5.0 → specmod-0.7.0}/stubs/obspy/core/util/__init__.pyi +0 -0
  183. {specmod-0.5.0 → specmod-0.7.0}/stubs/obspy/core/util/base.pyi +0 -0
  184. {specmod-0.5.0 → specmod-0.7.0}/stubs/obspy/geodetics/__init__.pyi +0 -0
  185. {specmod-0.5.0 → specmod-0.7.0}/stubs/obspy/signal/__init__.pyi +0 -0
  186. {specmod-0.5.0 → specmod-0.7.0}/stubs/obspy/signal/konnoohmachismoothing.pyi +0 -0
  187. {specmod-0.5.0 → specmod-0.7.0}/studies/magna_2020_paper.toml +0 -0
  188. {specmod-0.5.0 → specmod-0.7.0}/tests/__init__.py +0 -0
  189. {specmod-0.5.0 → specmod-0.7.0}/tests/conftest.py +0 -0
  190. {specmod-0.5.0 → specmod-0.7.0}/tests/golden/motion_reference.json +0 -0
  191. {specmod-0.5.0 → specmod-0.7.0}/tests/golden/pipeline_reference.json +0 -0
  192. {specmod-0.5.0 → specmod-0.7.0}/tests/golden/window_reference.json +0 -0
  193. {specmod-0.5.0 → specmod-0.7.0}/tests/test_ambient_state.py +0 -0
  194. {specmod-0.5.0 → specmod-0.7.0}/tests/test_api_surface.py +0 -0
  195. {specmod-0.5.0 → specmod-0.7.0}/tests/test_collection.py +0 -0
  196. {specmod-0.5.0 → specmod-0.7.0}/tests/test_cwt.py +0 -0
  197. {specmod-0.5.0 → specmod-0.7.0}/tests/test_datasets.py +0 -0
  198. {specmod-0.5.0 → specmod-0.7.0}/tests/test_docs_are_current.py +0 -0
  199. {specmod-0.5.0 → specmod-0.7.0}/tests/test_end_to_end.py +0 -0
  200. {specmod-0.5.0 → specmod-0.7.0}/tests/test_import.py +0 -0
  201. {specmod-0.5.0 → specmod-0.7.0}/tests/test_io_and_plotting.py +0 -0
  202. {specmod-0.5.0 → specmod-0.7.0}/tests/test_legacy_fixes.py +0 -0
  203. {specmod-0.5.0 → specmod-0.7.0}/tests/test_make_golden.py +0 -0
  204. {specmod-0.5.0 → specmod-0.7.0}/tests/test_pick_plugins.py +0 -0
  205. {specmod-0.5.0 → specmod-0.7.0}/tests/test_pick_readers.py +0 -0
  206. {specmod-0.5.0 → specmod-0.7.0}/tests/test_picks.py +0 -0
  207. {specmod-0.5.0 → specmod-0.7.0}/tests/test_pipeline_smoke.py +0 -0
  208. {specmod-0.5.0 → specmod-0.7.0}/tests/test_preprocess.py +0 -0
  209. {specmod-0.5.0 → specmod-0.7.0}/tests/test_prieto.py +0 -0
  210. {specmod-0.5.0 → specmod-0.7.0}/tests/test_quadratic.py +0 -0
  211. {specmod-0.5.0 → specmod-0.7.0}/tests/test_smoothing.py +0 -0
  212. {specmod-0.5.0 → specmod-0.7.0}/tests/test_sources.py +0 -0
  213. {specmod-0.5.0 → specmod-0.7.0}/tests/test_spectral_wiring.py +0 -0
  214. {specmod-0.5.0 → specmod-0.7.0}/tests/test_stubs.py +0 -0
  215. {specmod-0.5.0 → specmod-0.7.0}/tests/test_tools_track_the_api.py +0 -0
  216. {specmod-0.5.0 → specmod-0.7.0}/tests/test_transforms.py +0 -0
  217. {specmod-0.5.0 → specmod-0.7.0}/tests/test_tutorial.py +0 -0
  218. {specmod-0.5.0 → specmod-0.7.0}/tests/test_typing_backlog.py +0 -0
  219. {specmod-0.5.0 → specmod-0.7.0}/tests/test_utils.py +0 -0
  220. {specmod-0.5.0 → specmod-0.7.0}/tests/test_versioning.py +0 -0
  221. {specmod-0.5.0 → specmod-0.7.0}/tools/check_built_version.py +0 -0
  222. {specmod-0.5.0 → specmod-0.7.0}/tools/check_floors.py +0 -0
  223. {specmod-0.5.0 → specmod-0.7.0}/tools/make_logo.py +0 -0
  224. {specmod-0.5.0 → specmod-0.7.0}/tools/measure_docs.py +0 -0
  225. {specmod-0.5.0 → specmod-0.7.0}/tools/vendor_fonts.py +0 -0
  226. {specmod-0.5.0 → specmod-0.7.0}/tools/vendor_katex.py +0 -0
  227. /specmod-0.5.0/tutorial/data/events/2019-08-26T07:30:47.000000Z/picks/2019-08-26T07:30:47.000000Z.picks → /specmod-0.7.0/tutorial/data/events/20190826T073047.000000Z/picks/20190826T073047.000000Z.picks +0 -0
  228. /specmod-0.5.0/tutorial/data/events/2019-08-26T07:30:47.000000Z/picks/2019-08-26T07:30:47.000000Z.xml → /specmod-0.7.0/tutorial/data/events/20190826T073047.000000Z/picks/20190826T073047.000000Z.xml +0 -0
  229. {specmod-0.5.0/tutorial/data/events/2019-08-26T07:30:47.000000Z → specmod-0.7.0/tutorial/data/events/20190826T073047.000000Z}/stations/inventory.xml +0 -0
  230. /specmod-0.5.0/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/LV.L001..HHE_2019-08-26T07:30:47.000000Z → /specmod-0.7.0/tutorial/data/events/20190826T073047.000000Z/waveforms/LV.L001..HHE_20190826T073047.000000Z +0 -0
  231. /specmod-0.5.0/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/LV.L001..HHN_2019-08-26T07:30:47.000000Z → /specmod-0.7.0/tutorial/data/events/20190826T073047.000000Z/waveforms/LV.L001..HHN_20190826T073047.000000Z +0 -0
  232. /specmod-0.5.0/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/LV.L001..HHZ_2019-08-26T07:30:47.000000Z → /specmod-0.7.0/tutorial/data/events/20190826T073047.000000Z/waveforms/LV.L001..HHZ_20190826T073047.000000Z +0 -0
  233. /specmod-0.5.0/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/LV.L002..HHE_2019-08-26T07:30:47.000000Z → /specmod-0.7.0/tutorial/data/events/20190826T073047.000000Z/waveforms/LV.L002..HHE_20190826T073047.000000Z +0 -0
  234. /specmod-0.5.0/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/LV.L002..HHN_2019-08-26T07:30:47.000000Z → /specmod-0.7.0/tutorial/data/events/20190826T073047.000000Z/waveforms/LV.L002..HHN_20190826T073047.000000Z +0 -0
  235. /specmod-0.5.0/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/LV.L002..HHZ_2019-08-26T07:30:47.000000Z → /specmod-0.7.0/tutorial/data/events/20190826T073047.000000Z/waveforms/LV.L002..HHZ_20190826T073047.000000Z +0 -0
  236. /specmod-0.5.0/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/LV.L006..HHE_2019-08-26T07:30:47.000000Z → /specmod-0.7.0/tutorial/data/events/20190826T073047.000000Z/waveforms/LV.L006..HHE_20190826T073047.000000Z +0 -0
  237. /specmod-0.5.0/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/LV.L006..HHN_2019-08-26T07:30:47.000000Z → /specmod-0.7.0/tutorial/data/events/20190826T073047.000000Z/waveforms/LV.L006..HHN_20190826T073047.000000Z +0 -0
  238. /specmod-0.5.0/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/LV.L006..HHZ_2019-08-26T07:30:47.000000Z → /specmod-0.7.0/tutorial/data/events/20190826T073047.000000Z/waveforms/LV.L006..HHZ_20190826T073047.000000Z +0 -0
  239. /specmod-0.5.0/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/LV.L007..HHE_2019-08-26T07:30:47.000000Z → /specmod-0.7.0/tutorial/data/events/20190826T073047.000000Z/waveforms/LV.L007..HHE_20190826T073047.000000Z +0 -0
  240. /specmod-0.5.0/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/LV.L007..HHN_2019-08-26T07:30:47.000000Z → /specmod-0.7.0/tutorial/data/events/20190826T073047.000000Z/waveforms/LV.L007..HHN_20190826T073047.000000Z +0 -0
  241. /specmod-0.5.0/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/LV.L007..HHZ_2019-08-26T07:30:47.000000Z → /specmod-0.7.0/tutorial/data/events/20190826T073047.000000Z/waveforms/LV.L007..HHZ_20190826T073047.000000Z +0 -0
  242. /specmod-0.5.0/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/LV.L008..HHE_2019-08-26T07:30:47.000000Z → /specmod-0.7.0/tutorial/data/events/20190826T073047.000000Z/waveforms/LV.L008..HHE_20190826T073047.000000Z +0 -0
  243. /specmod-0.5.0/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/LV.L008..HHN_2019-08-26T07:30:47.000000Z → /specmod-0.7.0/tutorial/data/events/20190826T073047.000000Z/waveforms/LV.L008..HHN_20190826T073047.000000Z +0 -0
  244. /specmod-0.5.0/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/LV.L008..HHZ_2019-08-26T07:30:47.000000Z → /specmod-0.7.0/tutorial/data/events/20190826T073047.000000Z/waveforms/LV.L008..HHZ_20190826T073047.000000Z +0 -0
  245. /specmod-0.5.0/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/LV.L009..HHE_2019-08-26T07:30:47.000000Z → /specmod-0.7.0/tutorial/data/events/20190826T073047.000000Z/waveforms/LV.L009..HHE_20190826T073047.000000Z +0 -0
  246. /specmod-0.5.0/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/LV.L009..HHN_2019-08-26T07:30:47.000000Z → /specmod-0.7.0/tutorial/data/events/20190826T073047.000000Z/waveforms/LV.L009..HHN_20190826T073047.000000Z +0 -0
  247. /specmod-0.5.0/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/LV.L009..HHZ_2019-08-26T07:30:47.000000Z → /specmod-0.7.0/tutorial/data/events/20190826T073047.000000Z/waveforms/LV.L009..HHZ_20190826T073047.000000Z +0 -0
  248. /specmod-0.5.0/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/LV.LD06..HH1_2019-08-26T07:30:47.000000Z → /specmod-0.7.0/tutorial/data/events/20190826T073047.000000Z/waveforms/LV.LD06..HH1_20190826T073047.000000Z +0 -0
  249. /specmod-0.5.0/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/LV.LD06..HH2_2019-08-26T07:30:47.000000Z → /specmod-0.7.0/tutorial/data/events/20190826T073047.000000Z/waveforms/LV.LD06..HH2_20190826T073047.000000Z +0 -0
  250. /specmod-0.5.0/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/LV.LD06..HH3_2019-08-26T07:30:47.000000Z → /specmod-0.7.0/tutorial/data/events/20190826T073047.000000Z/waveforms/LV.LD06..HH3_20190826T073047.000000Z +0 -0
  251. /specmod-0.5.0/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/UR.AQ01.00.HHE_2019-08-26T07:30:47.000000Z → /specmod-0.7.0/tutorial/data/events/20190826T073047.000000Z/waveforms/UR.AQ01.00.HHE_20190826T073047.000000Z +0 -0
  252. /specmod-0.5.0/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/UR.AQ01.00.HHN_2019-08-26T07:30:47.000000Z → /specmod-0.7.0/tutorial/data/events/20190826T073047.000000Z/waveforms/UR.AQ01.00.HHN_20190826T073047.000000Z +0 -0
  253. /specmod-0.5.0/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/UR.AQ01.00.HHZ_2019-08-26T07:30:47.000000Z → /specmod-0.7.0/tutorial/data/events/20190826T073047.000000Z/waveforms/UR.AQ01.00.HHZ_20190826T073047.000000Z +0 -0
  254. /specmod-0.5.0/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/UR.AQ02.00.HHZ_2019-08-26T07:30:47.000000Z → /specmod-0.7.0/tutorial/data/events/20190826T073047.000000Z/waveforms/UR.AQ02.00.HHZ_20190826T073047.000000Z +0 -0
  255. /specmod-0.5.0/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/UR.AQ03.00.HHE_2019-08-26T07:30:47.000000Z → /specmod-0.7.0/tutorial/data/events/20190826T073047.000000Z/waveforms/UR.AQ03.00.HHE_20190826T073047.000000Z +0 -0
  256. /specmod-0.5.0/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/UR.AQ03.00.HHN_2019-08-26T07:30:47.000000Z → /specmod-0.7.0/tutorial/data/events/20190826T073047.000000Z/waveforms/UR.AQ03.00.HHN_20190826T073047.000000Z +0 -0
  257. /specmod-0.5.0/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/UR.AQ03.00.HHZ_2019-08-26T07:30:47.000000Z → /specmod-0.7.0/tutorial/data/events/20190826T073047.000000Z/waveforms/UR.AQ03.00.HHZ_20190826T073047.000000Z +0 -0
  258. /specmod-0.5.0/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/UR.AQ04.00.HHE_2019-08-26T07:30:47.000000Z → /specmod-0.7.0/tutorial/data/events/20190826T073047.000000Z/waveforms/UR.AQ04.00.HHE_20190826T073047.000000Z +0 -0
  259. /specmod-0.5.0/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/UR.AQ04.00.HHN_2019-08-26T07:30:47.000000Z → /specmod-0.7.0/tutorial/data/events/20190826T073047.000000Z/waveforms/UR.AQ04.00.HHN_20190826T073047.000000Z +0 -0
  260. /specmod-0.5.0/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/UR.AQ04.00.HHZ_2019-08-26T07:30:47.000000Z → /specmod-0.7.0/tutorial/data/events/20190826T073047.000000Z/waveforms/UR.AQ04.00.HHZ_20190826T073047.000000Z +0 -0
  261. /specmod-0.5.0/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/UR.AQ05.00.HHE_2019-08-26T07:30:47.000000Z → /specmod-0.7.0/tutorial/data/events/20190826T073047.000000Z/waveforms/UR.AQ05.00.HHE_20190826T073047.000000Z +0 -0
  262. /specmod-0.5.0/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/UR.AQ05.00.HHN_2019-08-26T07:30:47.000000Z → /specmod-0.7.0/tutorial/data/events/20190826T073047.000000Z/waveforms/UR.AQ05.00.HHN_20190826T073047.000000Z +0 -0
  263. /specmod-0.5.0/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/UR.AQ05.00.HHZ_2019-08-26T07:30:47.000000Z → /specmod-0.7.0/tutorial/data/events/20190826T073047.000000Z/waveforms/UR.AQ05.00.HHZ_20190826T073047.000000Z +0 -0
  264. /specmod-0.5.0/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/UR.AQ06.00.HHE_2019-08-26T07:30:47.000000Z → /specmod-0.7.0/tutorial/data/events/20190826T073047.000000Z/waveforms/UR.AQ06.00.HHE_20190826T073047.000000Z +0 -0
  265. /specmod-0.5.0/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/UR.AQ06.00.HHN_2019-08-26T07:30:47.000000Z → /specmod-0.7.0/tutorial/data/events/20190826T073047.000000Z/waveforms/UR.AQ06.00.HHN_20190826T073047.000000Z +0 -0
  266. /specmod-0.5.0/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/UR.AQ06.00.HHZ_2019-08-26T07:30:47.000000Z → /specmod-0.7.0/tutorial/data/events/20190826T073047.000000Z/waveforms/UR.AQ06.00.HHZ_20190826T073047.000000Z +0 -0
  267. /specmod-0.5.0/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/UR.AQ07.00.HHE_2019-08-26T07:30:47.000000Z → /specmod-0.7.0/tutorial/data/events/20190826T073047.000000Z/waveforms/UR.AQ07.00.HHE_20190826T073047.000000Z +0 -0
  268. /specmod-0.5.0/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/UR.AQ07.00.HHN_2019-08-26T07:30:47.000000Z → /specmod-0.7.0/tutorial/data/events/20190826T073047.000000Z/waveforms/UR.AQ07.00.HHN_20190826T073047.000000Z +0 -0
  269. /specmod-0.5.0/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/UR.AQ07.00.HHZ_2019-08-26T07:30:47.000000Z → /specmod-0.7.0/tutorial/data/events/20190826T073047.000000Z/waveforms/UR.AQ07.00.HHZ_20190826T073047.000000Z +0 -0
  270. /specmod-0.5.0/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/UR.AQ09.00.HHE_2019-08-26T07:30:47.000000Z → /specmod-0.7.0/tutorial/data/events/20190826T073047.000000Z/waveforms/UR.AQ09.00.HHE_20190826T073047.000000Z +0 -0
  271. /specmod-0.5.0/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/UR.AQ09.00.HHN_2019-08-26T07:30:47.000000Z → /specmod-0.7.0/tutorial/data/events/20190826T073047.000000Z/waveforms/UR.AQ09.00.HHN_20190826T073047.000000Z +0 -0
  272. /specmod-0.5.0/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/UR.AQ09.00.HHZ_2019-08-26T07:30:47.000000Z → /specmod-0.7.0/tutorial/data/events/20190826T073047.000000Z/waveforms/UR.AQ09.00.HHZ_20190826T073047.000000Z +0 -0
  273. /specmod-0.5.0/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/UR.AQ10.00.HHE_2019-08-26T07:30:47.000000Z → /specmod-0.7.0/tutorial/data/events/20190826T073047.000000Z/waveforms/UR.AQ10.00.HHE_20190826T073047.000000Z +0 -0
  274. /specmod-0.5.0/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/UR.AQ10.00.HHN_2019-08-26T07:30:47.000000Z → /specmod-0.7.0/tutorial/data/events/20190826T073047.000000Z/waveforms/UR.AQ10.00.HHN_20190826T073047.000000Z +0 -0
  275. /specmod-0.5.0/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/UR.AQ10.00.HHZ_2019-08-26T07:30:47.000000Z → /specmod-0.7.0/tutorial/data/events/20190826T073047.000000Z/waveforms/UR.AQ10.00.HHZ_20190826T073047.000000Z +0 -0
@@ -18,6 +18,8 @@ jobs:
18
18
  # fails every job at "Getting action download info", before a step runs.
19
19
  # Bump this deliberately; do not "tidy" it to `@v10`.
20
20
  - uses: astral-sh/setup-uv@v10.0.1
21
+ with:
22
+ version: "0.13.7" # see UV_VERSION in tests/test_release_config.py
21
23
  - run: uv python install 3.11
22
24
 
23
25
  - name: Build sdist and wheel
@@ -32,6 +32,8 @@ jobs:
32
32
  # fails every job at "Getting action download info", before a step runs.
33
33
  # Bump this deliberately; do not "tidy" it to `@v10`.
34
34
  - uses: astral-sh/setup-uv@v10.0.1
35
+ with:
36
+ version: "0.13.7" # see UV_VERSION in tests/test_release_config.py
35
37
  - run: uv python install 3.11
36
38
  - run: uv venv # create a venv to install into
37
39
 
@@ -0,0 +1,30 @@
1
+ name: pr-title
2
+
3
+ # A squash merge replaces the branch's commit subjects with this title, so the
4
+ # title is what release-please parses for the version bump and the changelog.
5
+ # `edited` is in the list because a title is most often fixed after the check
6
+ # has already failed once.
7
+ on:
8
+ pull_request:
9
+ types: [opened, edited, reopened]
10
+
11
+ # `pull_request`, not `pull_request_target`: this reads a title an outside
12
+ # contributor controls, and must never do so with a write token.
13
+ permissions: {}
14
+
15
+ concurrency:
16
+ group: pr-title-${{ github.event.pull_request.number }}
17
+ cancel-in-progress: true
18
+
19
+ jobs:
20
+ pr-title:
21
+ runs-on: ubuntu-latest
22
+ steps:
23
+ - uses: actions/checkout@v7
24
+ - name: The title must be a Conventional Commit
25
+ # Through the environment rather than as an argument: the title is
26
+ # attacker-chosen text, and `${{ }}` interpolation into a shell command
27
+ # would execute it.
28
+ env:
29
+ PR_TITLE: ${{ github.event.pull_request.title }}
30
+ run: python tools/check_pr_title.py
@@ -90,6 +90,8 @@ jobs:
90
90
  # fails every job at "Getting action download info", before a step runs.
91
91
  # Bump this deliberately; do not "tidy" it to `@v10`.
92
92
  - uses: astral-sh/setup-uv@v10.0.1
93
+ with:
94
+ version: "0.13.7" # see UV_VERSION in tests/test_release_config.py
93
95
  - run: uv python install 3.11
94
96
  - run: uv build
95
97
 
@@ -21,6 +21,7 @@ jobs:
21
21
  # Bump this deliberately; do not "tidy" it to `@v10`.
22
22
  - uses: astral-sh/setup-uv@v10.0.1
23
23
  with:
24
+ version: "0.13.7" # see UV_VERSION in tests/test_release_config.py
24
25
  enable-cache: true
25
26
  - run: uv python install 3.11
26
27
  - run: uv venv
@@ -41,6 +42,7 @@ jobs:
41
42
  # Bump this deliberately; do not "tidy" it to `@v10`.
42
43
  - uses: astral-sh/setup-uv@v10.0.1
43
44
  with:
45
+ version: "0.13.7" # see UV_VERSION in tests/test_release_config.py
44
46
  enable-cache: true
45
47
  - run: uv python install 3.11
46
48
  - run: uv venv
@@ -65,6 +67,7 @@ jobs:
65
67
  # Bump this deliberately; do not "tidy" it to `@v10`.
66
68
  - uses: astral-sh/setup-uv@v10.0.1
67
69
  with:
70
+ version: "0.13.7" # see UV_VERSION in tests/test_release_config.py
68
71
  enable-cache: true
69
72
  - run: uv python install ${{ matrix.python-version }}
70
73
  - run: uv venv
@@ -93,6 +96,8 @@ jobs:
93
96
  # fails every job at "Getting action download info", before a step runs.
94
97
  # Bump this deliberately; do not "tidy" it to `@v10`.
95
98
  - uses: astral-sh/setup-uv@v10.0.1
99
+ with:
100
+ version: "0.13.7" # see UV_VERSION in tests/test_release_config.py
96
101
  - run: uv python install 3.11
97
102
  - run: uv venv
98
103
  # Exercise the declared minimums. CI otherwise installs the newest of
@@ -117,6 +122,7 @@ jobs:
117
122
  # Bump this deliberately; do not "tidy" it to `@v10`.
118
123
  - uses: astral-sh/setup-uv@v10.0.1
119
124
  with:
125
+ version: "0.13.7" # see UV_VERSION in tests/test_release_config.py
120
126
  enable-cache: true
121
127
  - run: uv python install 3.11
122
128
  - run: uv venv
@@ -0,0 +1,3 @@
1
+ {
2
+ ".": "0.7.0"
3
+ }
@@ -41,6 +41,13 @@ to generate `CHANGELOG.md`. See
41
41
  - Say *why*, with the measurement if there was one. The history is the record
42
42
  of what was checked; a message that only restates the diff wastes it.
43
43
 
44
+ **The pull request title is one of them.** A squash merge discards every
45
+ subject on the branch and uses the title instead, composed by GitHub after all
46
+ local hooks have run, so the `commit-msg` hook never sees it. `pr-title.yml`
47
+ checks it against the same types `release-please-config.json` declares. Merging
48
+ with a merge commit keeps the individual subjects and is the safer default; the
49
+ check is what makes a squash safe when one is used anyway.
50
+
44
51
  ## Workflow files need a permission you may not have
45
52
 
46
53
  `.github/workflows/` is editable directly, but only when the session's GitHub
@@ -9,6 +9,51 @@ release could have this problem — every later one has a real predecessor tag
9
9
  so leave subsequent headings as generated. release-please only prepends to this
10
10
  file, so this edit survives. -->
11
11
 
12
+ ## [0.7.0](https://github.com/sgjholt/SpecMod/compare/v0.6.0...v0.7.0) (2026-11-08)
13
+
14
+
15
+ ### ⚠ BREAKING CHANGES
16
+
17
+ * **datasets:** event directories, waveform files and pick files are named in ISO 8601 basic format (`20190826T073047.000000Z`) instead of extended format (`2019-08-26T07:30:47.000000Z`), and `specmod fetch` writes the new names. Run `specmod migrate-names <dir>` on data fetched before 0.7; `Event.directory()` reads the old names with a DeprecationWarning until 0.8. Code that built a path from `Event.origin` or `SpectrumSet.event` should call `specmod.datasets.path_name` on it. See docs/upgrading.md, From 0.6 to 0.7.
18
+
19
+ ### Features
20
+
21
+ * **datasets:** name times on disk without colons, and migrate existing data ([52168de](https://github.com/sgjholt/SpecMod/commit/52168dea5cd9ffd8ff16dd9dd70996824c0bbb93))
22
+
23
+
24
+ ### Code Refactoring
25
+
26
+ * **config:** narrow the default_factory sentinel instead of suppressing mypy ([961da50](https://github.com/sgjholt/SpecMod/commit/961da50c28aa8ba785879a2b2287c219f64ec216))
27
+
28
+
29
+ ### Documentation
30
+
31
+ * **roadmap:** file the default change and the config pin as shipped in v0.6.0 ([92eff1a](https://github.com/sgjholt/SpecMod/commit/92eff1af91dceea5ee65da5a55b460c026cb75de))
32
+
33
+ ## [0.6.0](https://github.com/sgjholt/SpecMod/compare/v0.5.0...v0.6.0) (2026-09-27)
34
+
35
+ <!-- One Documentation line below was removed by hand, the third edit in this
36
+ file. release-please listed the roadmap entry twice: once for 9f35727, the
37
+ commit, and again for ed4e10c, the merge commit of #48, whose body repeated the
38
+ commit's subject and so parsed as a second commit. The entry kept points at the
39
+ commit itself. release-please only prepends to this file, so the edit
40
+ survives. -->
41
+
42
+ ### ⚠ BREAKING CHANGES
43
+
44
+ * **config:** default to FFT with Konno-Ohmachi smoothing
45
+
46
+ ### Features
47
+
48
+ * **config:** default to FFT with Konno-Ohmachi smoothing ([1081364](https://github.com/sgjholt/SpecMod/commit/108136484a55ea5cc6c3490ced0fc8cdaf0052b5))
49
+ * **config:** hold a configuration across an ambient read, and pin the goldens ([71a4e9c](https://github.com/sgjholt/SpecMod/commit/71a4e9c68c60c7ded332b6e2cdecd8da7c0b7a99))
50
+
51
+
52
+ ### Documentation
53
+
54
+ * **fitting:** say what the fit reads, and document it as a modelling choice ([33975ed](https://github.com/sgjholt/SpecMod/commit/33975ed1042e7374e7750c46aa879e666cf0296a))
55
+ * **roadmap:** file the station selection as shipped in v0.5.0 ([9f35727](https://github.com/sgjholt/SpecMod/commit/9f357276a6e6a7fd4694cd5107405b4d53f12f44))
56
+
12
57
  ## [0.5.0](https://github.com/sgjholt/SpecMod/compare/v0.4.0...v0.5.0) (2026-09-19)
13
58
 
14
59
  <!-- The `acquire` entries below are edited by hand, and are the second entry
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: specmod
3
- Version: 0.5.0
3
+ Version: 0.7.0
4
4
  Summary: A Python toolbox for processing and modelling seismic spectra
5
5
  Project-URL: Homepage, https://github.com/sgjholt/SpecMod
6
6
  Project-URL: Repository, https://github.com/sgjholt/SpecMod
@@ -3477,11 +3477,15 @@ bumping `smoothing.n_bins` from 151 to 158 and running the suite — and **9 of
3477
3477
  25 golden tests fail**, loudly. The golden values live in a committed JSON
3478
3478
  file, so any behaviour change breaks them whatever moved it.
3479
3479
 
3480
- The residual gap is narrower than the claim suggests but real: the golden file
3481
- records no config, so a *regenerated* reference silently adopts whatever
3482
- defaults were current. Pinning a study file, as this plan proposes, is what
3483
- would close it. Until then the protection is "the numbers are frozen", not
3484
- "the settings are frozen".
3480
+ **Closed in 0.6.** `specmod.config.using()` holds a configuration across the
3481
+ ambient reads the pipeline makes — a `ContextVar`, so it does not leak between
3482
+ threads, and explicit arguments still win — and `tests/golden/reference.toml`
3483
+ carries every setting the committed numbers were captured under, not an
3484
+ overlay that would inherit future default changes for whatever it omitted.
3485
+ `tools/make_golden.py` captures inside the pin and the golden suite compares
3486
+ inside it, so the property the claim asserted now holds: changing a shipped
3487
+ default leaves every committed number untouched. Measured by doing it — the
3488
+ 0.6 change of default moved 11 golden tests before the pin and none after.
3485
3489
 
3486
3490
  **Claims that were checked and hold:**
3487
3491
 
@@ -3717,33 +3721,57 @@ changes anything shipped.
3717
3721
 
3718
3722
  ### Still open
3719
3723
 
3720
- 1. **Default estimator.** Multitaper (matching current behaviour) or FFT +
3721
- Konno–Ohmachi (more conventional in engineering seismology)? **No longer
3722
- blocked** — §6.6's dead `[smoothing] method` is wired up, so the second
3723
- option is selectable — and now measured, on the 28 PNR windows with each
3724
- station fitted freely:
3725
-
3726
- | Against the shipped multitaper + `log_bins` | Median ΔMw | Per-station range | `fc` ratio |
3727
- |---|---|---|---|
3728
- | `fft` + `log_bins` | −0.039 | −0.85 to +1.90 | 0.003 to 5.0 |
3729
- | `fft` + `konno_ohmachi` | +0.027 | −0.52 to +0.51 | 0.038 to 3.2 |
3730
-
3731
- **The smoothing is what makes the switch defensible, not the estimator.**
3732
- FFT with the shipped binner moves a station by up to 1.9 magnitude units,
3733
- because `log_bins` reduces the axis without reducing the variance — 151 log
3734
- bins over a few hundred Fourier samples is roughly one sample per bin.
3735
- Pairing it with a real smoother halves the worst excursion and pulls the
3736
- `fc` range in by a third. The median station barely moves either way.
3737
-
3738
- What this event cannot settle: the *event* corner is unconstrained under all
3739
- three (stage-one spread 895% to 1042% of the event value), so the ensemble
3740
- number is not evidence. Deciding on the ensemble needs an event where it is
3741
- constrained.
3724
+ 1. ~~**Default estimator.**~~ **Resolved: FFT + Konno-Ohmachi**, from 0.6. The
3725
+ measurement below is what decided it, on the 28 PNR channels, each fitted
3726
+ independently (stage one) on velocity, which is the motion the pipeline fits
3727
+ by default and from which `llpsp` is the displacement plateau:
3728
+
3729
+ | Against the shipped multitaper + `log_bins` | Median ΔMw | Per-station ΔMw | `fc` ratio, median (range) | Median band (decades) | Median points fitted |
3730
+ |---|---|---|---|---|---|
3731
+ | `multitaper` + `log_bins` | — | — | — | 1.73 | 113 |
3732
+ | `fft` + `log_bins` | −0.079 | −0.71 to +0.23 | 0.91 (0.18 to 3.9) | 1.55 | 107 |
3733
+ | `fft` + `konno_ohmachi` | −0.068 | −0.25 to +0.19 | 0.82 (0.17 to 3.5) | 1.68 | 120 |
3734
+ | `fft` + `konno_ohmachi`, `fit_bins = true` | −0.073 | −0.25 to +0.15 | 0.90 (0.18 to 2.2) | 1.68 | 120 |
3735
+
3736
+ **What each setting reaches.** The estimator sets the spectrum that is
3737
+ fitted. The smoother sets the band: the signal-to-noise ratio and the gate
3738
+ are computed on smoothed spectra. With `[fitting] fit_bins = false`, the
3739
+ default in 0.1.1 and since, the fit and the initial guess both read the
3740
+ *unsmoothed* amplitudes inside that band, so the smoother reaches `Omega`
3741
+ only through the band. The two default-path FFT rows therefore fit identical
3742
+ amplitudes, and the whole difference between them — the per-station range
3743
+ narrowing from −0.71/+0.23 to −0.25/+0.19 — comes from Konno–Ohmachi
3744
+ selecting a wider band. Fitting the smoothed spectrum instead
3745
+ (`fit_bins = true`) changes little further on this event.
3746
+
3747
+ Measured on displacement instead, the same comparison gives −0.85/+0.70 for
3748
+ `fft` + `log_bins` and −0.56/+0.52 for `fft` + `konno_ohmachi`: the same
3749
+ ordering, a wider spread.
3750
+
3751
+ What this event cannot settle, and what the decision therefore does not
3752
+ rest on: the *event* corner is unconstrained under all three (stage-one
3753
+ spread 895% to 1042% of the event value), so the ensemble number is not
3754
+ evidence. On the ensemble, PNR's Mw moves −0.1615 under both constant sets
3755
+ — a pure Omega shift, since the constants are an offset applied afterwards.
3756
+ Against the catalogue's 2.9 the two sets move in opposite directions, the
3757
+ default constants from 0.194 to 0.032 away and §4.7's from 0.160 to 0.321,
3758
+ so they no longer both sit within 0.25. Recorded, not offered as evidence.
3742
3759
 
3743
3760
  Speed, measured rather than assumed and far too small to decide on: the
3744
3761
  transform is 0.209 s against 0.323 s for the 28 windows, and the smoothing
3745
3762
  adds 0.14 s (`log_bins`) to 0.83 s (`konno_ohmachi`) on top.
3746
3763
 
3764
+ **What had to be built first.** Changing a default moved 11 golden tests,
3765
+ because §6.6's config pin was a claim with no mechanism: the references were
3766
+ captured under whatever the defaults were. `config.using()` and
3767
+ `tests/golden/reference.toml` close that, so the references now hold their
3768
+ own settings and a default change leaves every committed number untouched.
3769
+
3770
+ **The fitting choice is documented where it is made.** Whether the fit
3771
+ reads the unsmoothed or the smoothed spectrum is a modelling decision a user
3772
+ should make knowingly, so the tutorial's *What the fit reads* states the
3773
+ default and fits the event both ways.
3774
+
3747
3775
  2. **Python floor.** 3.11 is proposed. Any users stuck on 3.9/3.10?
3748
3776
  3. **History rewrite.** Deleting the 9.9 MB catalog and stripping the notebook
3749
3777
  (§5.1) shrinks the *working tree* but leaves both in history, so a fresh clone
@@ -91,13 +91,15 @@ from pandas.core.series import Series
91
91
  import specmod.preprocess as pre
92
92
  import specmod.utils as ut
93
93
  from specmod.core.collection import SpectrumPair, SpectrumSet
94
+ from specmod.datasets import path_name
94
95
  from specmod.pipeline import spectrum_set_from_streams
95
96
  from specmod.staged import StagedFit
96
97
 
97
98
  # Relative to this notebook, so it runs from wherever it is opened.
98
99
  EVENTS = Path("data/events")
99
100
  EVENT = "2019-08-26T07:30:47.000000Z" # Event origin time
100
- DATA: Path = EVENTS / EVENT
101
+ # Named without colons, which Windows does not allow: 20190826T073047.000000Z.
102
+ DATA: Path = EVENTS / path_name(EVENT)
101
103
  WAVEFORMS: Path = DATA / "waveforms"
102
104
  STATIONS: Path = DATA / "stations" / "*.xml"
103
105
  PICKS: Path = DATA / "picks"
@@ -237,12 +239,14 @@ spectrum_set_from_streams
237
239
  ```
238
240
 
239
241
  `spectrum_set_from_streams` transforms both windows, puts the noise on the
240
- signal's frequency axis, bins both, raises the noise to account for what sits
241
- *under* the signal, takes the ratio and selects the band where it passes.
242
+ signal's frequency axis, smooths both, raises the noise to account for what
243
+ sits *under* the signal, takes the ratio and selects the band where it passes.
244
+ The smoothing is Konno–Ohmachi by default (`[smoothing] method`). It is used
245
+ for the ratio and the band, not for the fit — see *What the fit reads* below.
242
246
 
243
247
  Which estimator is used is configuration, not code: `fft`, `welch`,
244
248
  `multitaper`, `quadratic` and `cwt` all satisfy the same Parseval contract, so
245
- they are interchangeable here. The shipped default is `multitaper`.
249
+ they are interchangeable here. The shipped default is `fft`.
246
250
  """)
247
251
 
248
252
 
@@ -256,7 +260,7 @@ print(
256
260
 
257
261
 
258
262
  code("""
259
- # One station in detail: signal, noise, the binned spectra the ratio is
263
+ # One station in detail: signal, noise, the smoothed spectra the ratio is
260
264
  # actually computed on, the selected band (red) and the resolution floor (grey).
261
265
  from specmod.plotting import plot_pair, plot_set
262
266
 
@@ -453,9 +457,10 @@ displacement = spectra.to_motion("displacement")
453
457
  print(f"velocity {spectra[station].signal.unit}")
454
458
  print(f"displacement {displacement[station].signal.unit}")
455
459
 
456
- # The unbinned signal-to-noise ratio is invariant under this — both spectra are
457
- # divided by the same 2*pi*f — but the *binned* ratio is not, because a bin
458
- # holds a geometric mean. So a few bands do move.
460
+ # The unsmoothed signal-to-noise ratio is invariant under this — both spectra
461
+ # are divided by the same 2*pi*f — but the smoothed ratio is not: Konno-Ohmachi
462
+ # averages amplitudes across a window over which 2*pi*f changes, so some
463
+ # bands move.
459
464
  moved = [i for i in spectra.ids() if spectra[i].band != displacement[i].band]
460
465
  print(f"bands that moved: {len(moved)} of {len(spectra)}")
461
466
  """)
@@ -473,9 +478,10 @@ from the largest amplitude inside the band, the corner from where that
473
478
  maximum falls.
474
479
 
475
480
  `FitSpectra(spectra)` then `fit_spectra()` is the whole thing. The minimiser
476
- (Powell), the `t*` lower bound and whether to fit the binned or unbinned
481
+ (Powell), the `t*` lower bound and whether to fit the smoothed or unsmoothed
477
482
  spectrum all come from `[fitting]` in the configuration, so the defaults are
478
- recorded rather than remembered.
483
+ recorded rather than remembered. The last of those is a modelling choice with
484
+ consequences; *What the fit reads* below compares the two.
479
485
  """)
480
486
 
481
487
 
@@ -551,6 +557,62 @@ fits.table[["id", "fc", "fc-stderr", "llpsp", "ts", "pass_fitting"]].head(10)
551
557
  """)
552
558
 
553
559
 
560
+ md("""
561
+ ### What the fit reads
562
+
563
+ The smoothed spectra choose the band — the signal-to-noise ratio and the gate
564
+ are computed on them — but **the fit reads the unsmoothed spectrum inside that
565
+ band**. Smoothing decides *where* the model is fitted, not *what* it is fitted
566
+ to. That is `[fitting] fit_bins = false`, the default, and it is a modelling
567
+ choice:
568
+
569
+ - **Unsmoothed** (the default). Every Fourier sample in the band, each with its
570
+ full scatter. The samples are evenly spaced in Hz, so most of them sit in the
571
+ top decade of the band and dominate an unweighted misfit;
572
+ `[fitting] weight_method = "log"` weights residuals by `1/f` to shift that.
573
+ - **Smoothed** (`fit_bins = true`). The spectrum the band was chosen on. Under
574
+ Konno–Ohmachi that is the same frequencies with the scatter averaged down,
575
+ but neighbouring points share samples, so they are not independent and the
576
+ misfit counts the same information more than once. Under `log_bins` it is
577
+ one value per occupied bin, evenly spaced in log frequency.
578
+
579
+ The cell below fits the event both ways.
580
+ """)
581
+
582
+
583
+ code("""
584
+ smoothed_fits = FitSpectra(spectra, fit_bins=True)
585
+ smoothed_fits.fit_spectra()
586
+
587
+ first = spectra.ids()[0]
588
+ raw_fit, smoothed_fit = fits.models[first], smoothed_fits.models[first]
589
+ low, high = raw_fit.mod_freq[0], raw_fit.mod_freq[-1]
590
+ top_decade = np.mean(raw_fit.mod_freq >= high / 10)
591
+ print(
592
+ f"{first}: {raw_fit.mod_freq.size} points unsmoothed, "
593
+ f"{smoothed_fit.mod_freq.size} smoothed, over {low:.2f}-{high:.2f} Hz"
594
+ )
595
+ print(f" share of the points in the band's top decade: {top_decade:.0%}")
596
+ print(
597
+ " median |difference| in the log10 amplitudes the two fits see: "
598
+ f"{np.median(np.abs(raw_fit.mod_amp - smoothed_fit.mod_amp)):.3f}"
599
+ )
600
+
601
+ raw_table = fits.table.set_index("id")
602
+ smoothed_table = smoothed_fits.table.set_index("id")
603
+ d_plateau = (smoothed_table["llpsp"] - raw_table["llpsp"]).abs()
604
+ fc_ratio = smoothed_table["fc"] / raw_table["fc"]
605
+ print(f"\\nacross {len(raw_table)} channels, smoothed against unsmoothed:")
606
+ print(
607
+ f" |d log10 plateau| median {d_plateau.median():.3f}, max {d_plateau.max():.3f}"
608
+ )
609
+ print(
610
+ f" fc ratio median {fc_ratio.median():.2f}, "
611
+ f"range {fc_ratio.min():.2f} to {fc_ratio.max():.2f}"
612
+ )
613
+ """)
614
+
615
+
554
616
  md("""
555
617
  ### The fit is not unique, and that is not a detail
556
618
 
@@ -944,7 +1006,7 @@ it, because journal supplements want one.
944
1006
  code("""
945
1007
  from specmod.io import load, save
946
1008
 
947
- path: Path = save(path=OUTPUT / spectra.event, spectra=spectra)
1009
+ path: Path = save(path=OUTPUT / path_name(spectra.event), spectra=spectra)
948
1010
  print(f"{path} ({path.stat().st_size // 1024} KB)")
949
1011
 
950
1012
  back: SpectrumSet = load(path)
@@ -956,8 +1018,8 @@ print(
956
1018
 
957
1019
 
958
1020
  code("""
959
- FitSpectra.write_flatfile(path=OUTPUT / "flatfiles" / f"{spectra.event}.parquet", fits=fits)
960
- FitSpectra.write_flatfile(path=OUTPUT / "flatfiles" / f"{spectra.event}.csv", fits=fits)
1021
+ FitSpectra.write_flatfile(path=OUTPUT / "flatfiles" / f"{path_name(spectra.event)}.parquet", fits=fits)
1022
+ FitSpectra.write_flatfile(path=OUTPUT / "flatfiles" / f"{path_name(spectra.event)}.csv", fits=fits)
961
1023
  sorted(p.name for p in (OUTPUT / "flatfiles").iterdir())
962
1024
  """)
963
1025
 
@@ -30,6 +30,13 @@ station wildcards against the data centre, cuts each channel to the window
30
30
  relative to origin, and writes an `EventDirectory` with a `manifest.json`
31
31
  beside it.
32
32
 
33
+ The event directory and each waveform file carry the origin in ISO 8601 basic
34
+ format — `20200318T130931.000000Z`, not `2020-03-18T13:09:31.000000Z` — because
35
+ the colons are not legal in a Windows file name, and a source distribution or
36
+ archive containing one is refused by uv 0.13. A fetch made before 0.7 has the
37
+ old names: run `specmod migrate-names build/magna_2020` before archiving it,
38
+ which renames them and updates the manifest to match.
39
+
33
40
  Expect it to take a few minutes — Magna is 88 stations across seven networks.
34
41
 
35
42
  **Check the manifest before going further.** It records what came back, not
@@ -171,9 +171,7 @@ Fourier grid is uniform in Hz. Weighting samples equally tilts the result by
171
171
  axis brings that to 3.2e-8 dex. The latter is the default. Konno–Ohmachi
172
172
  applies the former, by definition, and keeps it.
173
173
 
174
- ## In progress — not yet released
175
-
176
- Merged on `main`, and it will name its version when a release goes out.
174
+ ## Shipped in v0.5.0
177
175
 
178
176
  ### The station selection means what the config says
179
177
 
@@ -193,6 +191,56 @@ found by a live fetch rather than by the suite:
193
191
  as two independent records of one ground motion. `channel_priorities` and
194
192
  `location_priorities` rank them, first match wins per station.
195
193
 
194
+ ## Shipped in v0.6.0
195
+
196
+ ### The default is FFT with Konno-Ohmachi smoothing
197
+
198
+ The shipped pair was multitaper with log-binning. Measured on the 28 PNR
199
+ channels, each fitted independently on velocity, against that pair:
200
+
201
+ | | Median ΔMw | Per-station ΔMw | `fc` ratio, median (range) |
202
+ |---|---|---|---|
203
+ | `fft` + `log_bins` | −0.079 | −0.71 to +0.23 | 0.91 (0.18 to 3.9) |
204
+ | `fft` + `konno_ohmachi` | −0.068 | −0.25 to +0.19 | 0.82 (0.17 to 3.5) |
205
+
206
+ By default the fit reads the unsmoothed spectrum inside the band the smoothed
207
+ signal-to-noise ratio selects, so the two FFT rows fit identical amplitudes
208
+ and differ only in the band. Konno–Ohmachi's is wider, and that is what
209
+ narrows the per-station spread. This event's corner is unconstrained under
210
+ every pair, so the event-level number is not evidence either way.
211
+
212
+ **Every result moves** unless a configuration already pinned both settings.
213
+ [Upgrading](upgrading.md#from-05-to-06) has what to pin to keep the old
214
+ numbers, and what `[fitting] fit_bins = true` changes: it fits the smoothed
215
+ spectrum instead of the unsmoothed one.
216
+
217
+ ### A configuration can be held across an ambient read
218
+
219
+ `config.using()` pins one configuration for everything inside the block,
220
+ including the many pipeline functions that read their settings from call sites
221
+ taking no configuration argument. That was the prerequisite for the change
222
+ above: the golden references were captured under whatever the defaults were,
223
+ so moving a default moved 11 committed numbers, and a deliberate change was
224
+ indistinguishable from a regression. They now carry their own settings in
225
+ `tests/golden/reference.toml`, and a default change leaves every one of them
226
+ untouched.
227
+
228
+ ## In progress — not yet released
229
+
230
+ Merged on `main`, and it will name its version when a release goes out.
231
+
232
+ ### Names on disk carry no colons
233
+
234
+ Event directories and the files in them were named by the origin as ObsPy
235
+ prints it, `2019-08-26T07:30:47.000000Z`. Windows does not allow the colons,
236
+ and uv 0.13 refuses to build a source distribution containing them, which
237
+ stopped the release. Names are now ISO 8601 basic format,
238
+ `20190826T073047.000000Z`, from `specmod.datasets.path_name`, and
239
+ `specmod fetch` writes them. `specmod migrate-names` renames data written
240
+ earlier and keeps its manifest verifying; until 0.8, data that has not been
241
+ migrated is still found, with a warning.
242
+ [Upgrading](upgrading.md#from-06-to-07) has the steps.
243
+
196
244
  ## Planned
197
245
 
198
246
  ### 1.0 — the API stops moving
@@ -223,6 +271,27 @@ the same code path. Nothing breaks at import or at the call; results move. That
223
271
  is a weaker promise than the one 1.0 makes and a real one to have broken, so
224
272
  the count starts again rather than standing at one.
225
273
 
274
+ **v0.5.0 is not clean either, in the same way.** Nothing was renamed and no
275
+ signature moved. But `max_radius_km` now cuts against true WGS84 distance
276
+ rather than a fixed kilometres-per-degree, so the same config can select a
277
+ different set of stations than it did before, and three radius values that used
278
+ to be accepted — zero, negative, and a minimum outside the maximum — are now
279
+ refused when the config is read. A config is as much a caller as a call site.
280
+
281
+ Twice in a row makes it worth naming: what keeps moving is not the API, it is
282
+ what the API *does* with a setting that was already there. A deprecation cycle
283
+ does not protect against that, since there is nothing to deprecate; the
284
+ [upgrading guide](upgrading.md) is the only thing that does, and 1.0 will not
285
+ change that.
286
+
287
+ **v0.6.0 is not clean either, and is the first to break on purpose.** Its
288
+ default estimator and smoother changed, so every result moves unless a
289
+ configuration pinned both; the change is marked breaking, and
290
+ [Upgrading](upgrading.md#from-05-to-06) says what to pin. One signature moved
291
+ as well: the first argument of `load_config` is positional-only, so
292
+ `load_config(start=path)` is now read as a `[start]` configuration section and
293
+ refused. The count stands at zero.
294
+
226
295
  ## After 1.0
227
296
 
228
297
  Designed but deliberately not on the path to 1.0, because each is blocked on