spectro-kernel 0.9.0__tar.gz → 0.9.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 (400) hide show
  1. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/CHANGELOG.md +157 -0
  2. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/PKG-INFO +1 -1
  3. spectro_kernel-0.9.1/src/spectro_kernel/algorithms/_robust.py +29 -0
  4. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/activity/activity_index_caii_hk.py +3 -2
  5. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/advanced/doppler_tomogram.py +25 -17
  6. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/catalogs/gaia.py +38 -13
  7. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/catalogs/simbad.py +3 -3
  8. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/catalogs/vizier.py +11 -3
  9. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/classification/classify_template_chi2.py +12 -6
  10. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/continuum/normalize_spline.py +3 -2
  11. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/corrections/atmospheric_extinction.py +3 -3
  12. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/corrections/doppler_shift.py +2 -2
  13. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/corrections/fit_telluric_scaling.py +3 -2
  14. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/embedding/embed_band_power.py +2 -2
  15. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/embedding/embed_pretrained.py +15 -13
  16. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/embedding/embed_remote.py +3 -2
  17. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/embedding/embed_wavelets.py +7 -4
  18. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/exports/export_fits_bess.py +4 -4
  19. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/exports/export_hdf5.py +2 -2
  20. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/extraction/detect_trace.py +6 -5
  21. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/extraction/easyspec_extract.py +10 -5
  22. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/extraction/optimal.py +7 -7
  23. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/io/read_echelle.py +70 -16
  24. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/io/read_sdss.py +4 -3
  25. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/kinematics/vsini_fourier.py +2 -2
  26. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/lines/_line_flux.py +2 -1
  27. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/lines/compare_line_fits.py +4 -4
  28. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/lines/detect.py +64 -34
  29. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/lines/vr_ratio.py +5 -3
  30. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/nebular/bpt_line_ratios.py +18 -16
  31. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/nebular/sii_electron_density.py +3 -3
  32. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/quality/compare_snr_methods.py +2 -2
  33. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/quality/snr_der.py +2 -1
  34. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/reduction/bias_combine.py +3 -2
  35. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/reduction/clip_cosmic_rays.py +5 -3
  36. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/reduction/correct_smile_xcorr.py +2 -2
  37. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/reduction/denoise_2d.py +5 -5
  38. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/reduction/easyspec_cosmic_ray.py +6 -5
  39. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/reduction/easyspec_flat.py +3 -3
  40. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/reduction/extract_spectrum_sum.py +3 -3
  41. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/reduction/flat_combine.py +5 -5
  42. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/reduction/flat_normalize.py +5 -5
  43. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/reduction/geometry.py +21 -17
  44. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/reduction/subtract_sky_2d.py +2 -2
  45. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/reduction/wavelength_calibrate.py +5 -1
  46. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/rv/precision_bouchy.py +22 -8
  47. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/stacking/merge_echelle_orders.py +6 -4
  48. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/stacking/stack.py +2 -1
  49. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/timeseries/box_least_squares.py +2 -1
  50. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/timeseries/lomb_scargle.py +13 -12
  51. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/timeseries/phase_fold.py +1 -1
  52. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/timeseries/temporal_variance_spectrum.py +2 -1
  53. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/transforms/combine_arithmetic.py +4 -3
  54. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/viz/plot_dynamic_spectrum.py +5 -8
  55. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/viz/plot_plotly.py +6 -5
  56. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/wavelength_calibration/arc_geometry.py +31 -32
  57. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/wavelength_calibration/easyspec_wavelength.py +8 -4
  58. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/wavelength_calibration/in_situ.py +22 -19
  59. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/wavelength_calibration/lamp_atlas.py +140 -39
  60. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/wavelength_calibration/match_lamp.py +37 -27
  61. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/wavelength_calibration/refine_zeropoint.py +3 -2
  62. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/wavelength_calibration/reidentify.py +6 -12
  63. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/wavelength_calibration/solar.py +30 -26
  64. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/registry.py +12 -14
  65. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/version.py +1 -1
  66. spectro_kernel-0.9.1/tests/unit/test_audit_behaviour_fixes.py +120 -0
  67. spectro_kernel-0.9.1/tests/unit/test_calibration_flag_chain.py +47 -0
  68. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_catalog_queries_mocked.py +32 -15
  69. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_rv_precision_bouchy.py +32 -10
  70. spectro_kernel-0.9.1/tests/unit/test_source_path_layouts.py +42 -0
  71. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_uncertainty_propagation.py +1 -1
  72. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_viz.py +2 -1
  73. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tools/docs/netlify.toml +5 -3
  74. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/.gitignore +0 -0
  75. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/AGENTS.md +0 -0
  76. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/LICENSE +0 -0
  77. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/README.md +0 -0
  78. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/docs/.drafts/README.md +0 -0
  79. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/docs/.drafts/notebooks/aurora-line-monitor.md +0 -0
  80. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/docs/concepts/algorithms.md +0 -0
  81. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/docs/concepts/architecture.md +0 -0
  82. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/docs/concepts/data-types.md +0 -0
  83. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/docs/concepts/pipelines.md +0 -0
  84. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/docs/contributing.md +0 -0
  85. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/docs/cookbook/multi-star-viewer.md +0 -0
  86. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/docs/cookbook/web-playground.md +0 -0
  87. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/docs/getting-started.md +0 -0
  88. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/docs/images/notebooks/alpha_cyg_ew_timeseries.png +0 -0
  89. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/docs/images/notebooks/alpha_cyg_halpha_phase_stack.png +0 -0
  90. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/docs/images/notebooks/alpha_cyg_halpha_timeseries.png +0 -0
  91. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/docs/images/notebooks/alpha_cyg_overlay.png +0 -0
  92. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/docs/images/notebooks/alpha_cyg_pc1_vs_phase.png +0 -0
  93. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/docs/images/notebooks/alpha_cyg_pca_2d.png +0 -0
  94. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/docs/images/notebooks/alpha_cyg_pca_3d.png +0 -0
  95. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/docs/images/notebooks/alpha_cyg_periodogram.png +0 -0
  96. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/docs/images/notebooks/alpha_cyg_phase_folded.png +0 -0
  97. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/docs/images/notebooks/alpha_cyg_rv_timeseries.png +0 -0
  98. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/docs/images/notebooks/alpha_cyg_similarity.png +0 -0
  99. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/docs/images/notebooks/alpha_cyg_similarity_date.png +0 -0
  100. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/docs/images/notebooks/alpha_dra_halpha_phase_stack.png +0 -0
  101. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/docs/images/notebooks/alpha_dra_pc1_vs_phase.png +0 -0
  102. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/docs/images/notebooks/alpha_dra_pca_2d.png +0 -0
  103. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/docs/images/notebooks/alpha_dra_pca_3d.png +0 -0
  104. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/docs/images/notebooks/alpha_dra_periodogram.png +0 -0
  105. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/docs/images/notebooks/alpha_dra_phase_folded.png +0 -0
  106. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/docs/images/notebooks/alpha_dra_rv_timeseries.png +0 -0
  107. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/docs/images/notebooks/alpha_dra_similarity_date.png +0 -0
  108. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/docs/images/notebooks/alpha_dra_similarity_phase.png +0 -0
  109. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/docs/index.md +0 -0
  110. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/docs/notebooks/alpha-cyg-time-series.md +0 -0
  111. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/docs/notebooks/alpha-dra-binary-period.md +0 -0
  112. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/docs/notebooks/be-star-variability.md +0 -0
  113. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/docs/notebooks/claude-mcp-end-to-end.md +0 -0
  114. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/docs/notebooks/exoplanet-transit-rv.md +0 -0
  115. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/docs/notebooks/full-reduction-walkthrough.md +0 -0
  116. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/docs/notebooks/native-vs-easyspec-showdown.md +0 -0
  117. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/docs/notebooks/your-first-sb2.md +0 -0
  118. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/docs/recipes/format.md +0 -0
  119. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/docs/recipes/index.md +0 -0
  120. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/docs/recipes/share.md +0 -0
  121. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/docs/reference/python.md +0 -0
  122. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/docs/tutorials/add-an-algorithm.md +0 -0
  123. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/docs/tutorials/analyse-a-spectrum.md +0 -0
  124. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/docs/tutorials/discover-the-catalogue.md +0 -0
  125. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/docs/tutorials/first-spectrum.md +0 -0
  126. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/docs/tutorials/long-slit-reduction.md +0 -0
  127. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/docs/usage/cli.md +0 -0
  128. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/docs/usage/library.md +0 -0
  129. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/docs/usage/mcp.md +0 -0
  130. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/docs/validation/.nav.md +0 -0
  131. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/docs/validation/index.md +0 -0
  132. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/docs/why.md +0 -0
  133. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/examples/longslit_selfcalibrated.py +0 -0
  134. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/examples/mcp_client.py +0 -0
  135. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/examples/quickstart.py +0 -0
  136. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/examples/recipe_with_profile.py +0 -0
  137. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/examples/wavelength_calibration.py +0 -0
  138. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/pyproject.toml +0 -0
  139. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/__init__.py +0 -0
  140. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/adapters/__init__.py +0 -0
  141. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/adapters/easyspec.py +0 -0
  142. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/__init__.py +0 -0
  143. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/_common.py +0 -0
  144. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/activity/__init__.py +0 -0
  145. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/activity/_bands.py +0 -0
  146. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/activity/activity_index_halpha.py +0 -0
  147. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/activity/ccf_bisector.py +0 -0
  148. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/advanced/__init__.py +0 -0
  149. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/advanced/aperture_photometry.py +0 -0
  150. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/advanced/disentangle_sb2.py +0 -0
  151. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/catalogs/__init__.py +0 -0
  152. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/catalogs/standard_star.py +0 -0
  153. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/classification/__init__.py +0 -0
  154. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/classification/pickles_atlas.py +0 -0
  155. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/continuum/__init__.py +0 -0
  156. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/continuum/compare_normalisations.py +0 -0
  157. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/continuum/normalize_edges.py +0 -0
  158. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/continuum/normalize_max.py +0 -0
  159. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/continuum/normalize_percentile.py +0 -0
  160. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/continuum/normalize_polynomial.py +0 -0
  161. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/continuum/normalize_region.py +0 -0
  162. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/continuum/subtract_continuum.py +0 -0
  163. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/corrections/__init__.py +0 -0
  164. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/corrections/air_vacuum.py +0 -0
  165. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/corrections/barycentric.py +0 -0
  166. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/corrections/convert_flux_to_rayleigh.py +0 -0
  167. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/corrections/deredden_interstellar.py +0 -0
  168. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/corrections/extinction_correct_easyspec.py +0 -0
  169. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/corrections/flux_calibrate_easyspec.py +0 -0
  170. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/corrections/remove_telluric.py +0 -0
  171. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/corrections/response_from_standard.py +0 -0
  172. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/corrections/synth_telluric.py +0 -0
  173. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/embedding/__init__.py +0 -0
  174. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/embedding/embed_continuum_subtracted.py +0 -0
  175. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/embedding/embed_lick_indices.py +0 -0
  176. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/embedding/embed_log_lambda.py +0 -0
  177. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/embedding/embed_spectrum.py +0 -0
  178. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/exports/__init__.py +0 -0
  179. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/exports/_bess_contract.py +0 -0
  180. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/exports/export_csv.py +0 -0
  181. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/exports/export_fits.py +0 -0
  182. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/exports/export_votable.py +0 -0
  183. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/extraction/__init__.py +0 -0
  184. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/extraction/_aperture.py +0 -0
  185. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/extraction/boxcar.py +0 -0
  186. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/extraction/sky_lateral_bands.py +0 -0
  187. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/io/__init__.py +0 -0
  188. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/io/read_ascii.py +0 -0
  189. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/io/read_fits.py +0 -0
  190. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/io/read_votable.py +0 -0
  191. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/kinematics/__init__.py +0 -0
  192. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/kinematics/rotation_curve.py +0 -0
  193. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/lines/__init__.py +0 -0
  194. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/lines/_profiles.py +0 -0
  195. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/lines/catalogs.py +0 -0
  196. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/lines/equivalent_width.py +0 -0
  197. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/lines/fit_gaussian.py +0 -0
  198. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/lines/fit_lorentzian.py +0 -0
  199. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/lines/fit_voigt.py +0 -0
  200. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/nebular/__init__.py +0 -0
  201. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/nebular/balmer_decrement_extinction.py +0 -0
  202. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/nebular/oiii_electron_temperature.py +0 -0
  203. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/quality/__init__.py +0 -0
  204. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/quality/measure_line_offsets.py +0 -0
  205. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/quality/measure_resolving_power.py +0 -0
  206. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/quality/snr_edge.py +0 -0
  207. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/quality/snr_linear_fit.py +0 -0
  208. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/quality/validate_bess_header.py +0 -0
  209. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/reduction/__init__.py +0 -0
  210. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/reduction/_easyspec_apply.py +0 -0
  211. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/reduction/_easyspec_helpers.py +0 -0
  212. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/reduction/dark_combine.py +0 -0
  213. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/reduction/dark_subtract.py +0 -0
  214. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/reduction/easyspec_bias.py +0 -0
  215. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/reduction/easyspec_dark.py +0 -0
  216. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/reduction/easyspec_flat_normalize.py +0 -0
  217. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/reduction/easyspec_subtract_bias.py +0 -0
  218. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/reduction/easyspec_subtract_dark.py +0 -0
  219. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/reduction/flat_normalize_response.py +0 -0
  220. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/reduction/subtract_bias.py +0 -0
  221. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/rv/__init__.py +0 -0
  222. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/rv/cross_correlate.py +0 -0
  223. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/rv/fit_keplerian_orbit.py +0 -0
  224. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/rv/measure.py +0 -0
  225. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/rv/redshift_lines.py +0 -0
  226. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/smoothing/__init__.py +0 -0
  227. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/smoothing/_kernel_uncertainty.py +0 -0
  228. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/smoothing/_nan_bridge.py +0 -0
  229. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/smoothing/compare_smoothings.py +0 -0
  230. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/smoothing/smooth_gaussian.py +0 -0
  231. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/smoothing/smooth_savgol.py +0 -0
  232. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/stacking/__init__.py +0 -0
  233. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/timeseries/__init__.py +0 -0
  234. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/timeseries/_light_curve.py +0 -0
  235. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/timeseries/phase_dispersion_minimization.py +0 -0
  236. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/transforms/__init__.py +0 -0
  237. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/transforms/clip_sigma.py +0 -0
  238. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/transforms/extract_region.py +0 -0
  239. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/transforms/mask_range.py +0 -0
  240. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/transforms/resample.py +0 -0
  241. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/transforms/resample_flux_conserving.py +0 -0
  242. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/viz/__init__.py +0 -0
  243. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/viz/plot_3d_surface.py +0 -0
  244. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/viz/plot_animation.py +0 -0
  245. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/wavelength_calibration/__init__.py +0 -0
  246. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/algorithms/wavelength_calibration/_line_fit.py +0 -0
  247. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/base.py +0 -0
  248. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/cli.py +0 -0
  249. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/embeddings.py +0 -0
  250. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/errors.py +0 -0
  251. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/io/__init__.py +0 -0
  252. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/io/ascii.py +0 -0
  253. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/io/fits.py +0 -0
  254. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/io/votable.py +0 -0
  255. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/pipeline.py +0 -0
  256. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/presets/__init__.py +0 -0
  257. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/presets/catalog/analysis/balmer_quick.yaml +0 -0
  258. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/presets/catalog/analysis/embed_quick.yaml +0 -0
  259. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/presets/catalog/analysis/quality_report.yaml +0 -0
  260. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/presets/catalog/analysis/rv_quick.yaml +0 -0
  261. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/presets/catalog/analysis/snr_check.yaml +0 -0
  262. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/presets/catalog/analysis/time_series_overview.yaml +0 -0
  263. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/presets/catalog/reduction/full_reduction_easyspec.yaml +0 -0
  264. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/presets/loader.py +0 -0
  265. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/presets/preset.schema.json +0 -0
  266. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/presets/variables.py +0 -0
  267. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/py.typed +0 -0
  268. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/registry_export.py +0 -0
  269. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/types/__init__.py +0 -0
  270. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/types/catalog.py +0 -0
  271. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/types/context.py +0 -0
  272. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/types/enums.py +0 -0
  273. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/types/history.py +0 -0
  274. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/types/image.py +0 -0
  275. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/types/line.py +0 -0
  276. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/types/spectrum.py +0 -0
  277. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/types/timeseries.py +0 -0
  278. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_kernel/url_safety.py +0 -0
  279. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_mcp/__init__.py +0 -0
  280. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_mcp/__main__.py +0 -0
  281. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_mcp/auth.py +0 -0
  282. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_mcp/auto_tools.py +0 -0
  283. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_mcp/observability.py +0 -0
  284. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_mcp/py.typed +0 -0
  285. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_mcp/resources.py +0 -0
  286. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_mcp/server.py +0 -0
  287. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_mcp/session.py +0 -0
  288. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/src/spectro_mcp/url_safety.py +0 -0
  289. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/conftest.py +0 -0
  290. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/reference/cases.py +0 -0
  291. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/reference/conftest.py +0 -0
  292. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/reference/test_known_answers.py +0 -0
  293. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/reference/test_sun.py +0 -0
  294. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/reference/test_validation_cases.py +0 -0
  295. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/reference/test_vega.py +0 -0
  296. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_activity_bisector.py +0 -0
  297. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_activity_caii_hk.py +0 -0
  298. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_activity_halpha.py +0 -0
  299. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_aperture_photometry.py +0 -0
  300. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_barycentric_bjd.py +0 -0
  301. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_base.py +0 -0
  302. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_classify_template_chi2.py +0 -0
  303. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_cli.py +0 -0
  304. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_combine.py +0 -0
  305. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_combine_arithmetic.py +0 -0
  306. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_continuum.py +0 -0
  307. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_convert_flux_to_rayleigh.py +0 -0
  308. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_correct_smile_xcorr.py +0 -0
  309. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_corrections.py +0 -0
  310. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_cross_correlate_extension.py +0 -0
  311. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_dark_flat_combine.py +0 -0
  312. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_denoise_2d.py +0 -0
  313. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_detect_lines_blind.py +0 -0
  314. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_detect_trace.py +0 -0
  315. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_disentangle_sb2.py +0 -0
  316. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_docs_generators.py +0 -0
  317. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_doppler_tomogram.py +0 -0
  318. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_easyspec_apply_staging.py +0 -0
  319. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_easyspec_sessions.py +0 -0
  320. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_easyspec_wrappers.py +0 -0
  321. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_embedding.py +0 -0
  322. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_embedding_pretrained.py +0 -0
  323. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_embedding_remote_lick.py +0 -0
  324. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_embedding_tier1.py +0 -0
  325. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_examples_run.py +0 -0
  326. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_export_fits_bess.py +0 -0
  327. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_export_hdf5.py +0 -0
  328. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_extract_boxcar.py +0 -0
  329. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_extract_boxcar_fixed.py +0 -0
  330. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_extract_optimal.py +0 -0
  331. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_extract_spectrum_sum.py +0 -0
  332. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_extraction_centring.py +0 -0
  333. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_flat_normalize_response.py +0 -0
  334. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_flux_atmospheric_extinction.py +0 -0
  335. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_flux_balmer_decrement.py +0 -0
  336. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_flux_calibrate_easyspec.py +0 -0
  337. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_flux_deredden_interstellar.py +0 -0
  338. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_flux_standard_star_reference.py +0 -0
  339. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_geometry_corrections.py +0 -0
  340. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_io.py +0 -0
  341. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_io_fits_zoo.py +0 -0
  342. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_lamp_atlas_match.py +0 -0
  343. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_line_profiles.py +0 -0
  344. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_lines.py +0 -0
  345. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_mask_honoured.py +0 -0
  346. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_mcp.py +0 -0
  347. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_mcp_http_entry.py +0 -0
  348. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_mcp_param_guard.py +0 -0
  349. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_mcp_production.py +0 -0
  350. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_mcp_resources.py +0 -0
  351. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_mcp_security.py +0 -0
  352. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_mcp_tools_extra.py +0 -0
  353. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_measure_arc_geometry.py +0 -0
  354. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_measure_line_offsets.py +0 -0
  355. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_misc_algorithms.py +0 -0
  356. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_nebular_diagnostics.py +0 -0
  357. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_no_circular_imports.py +0 -0
  358. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_normalize_spline.py +0 -0
  359. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_normalize_to_region.py +0 -0
  360. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_normalize_to_region_guard.py +0 -0
  361. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_output_produces_contract.py +0 -0
  362. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_pipeline.py +0 -0
  363. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_presets.py +0 -0
  364. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_quality.py +0 -0
  365. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_read_sdss.py +0 -0
  366. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_redshift_lines.py +0 -0
  367. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_refine_wavelength_zeropoint.py +0 -0
  368. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_registry.py +0 -0
  369. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_reidentify.py +0 -0
  370. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_resample_flux_conserving.py +0 -0
  371. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_resample_oversampling.py +0 -0
  372. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_resolving_power.py +0 -0
  373. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_response_from_standard.py +0 -0
  374. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_rotation_curve.py +0 -0
  375. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_sky_lateral_bands.py +0 -0
  376. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_smoothing.py +0 -0
  377. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_subtract_bias.py +0 -0
  378. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_subtract_bias_margin.py +0 -0
  379. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_timeseries.py +0 -0
  380. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_timeseries_bls.py +0 -0
  381. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_timeseries_pdm.py +0 -0
  382. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_timeseries_tvs.py +0 -0
  383. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_transforms.py +0 -0
  384. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_types.py +0 -0
  385. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_url_safety_kernel.py +0 -0
  386. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_validate_bess_header.py +0 -0
  387. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_vr_ratio.py +0 -0
  388. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_vsini_fourier.py +0 -0
  389. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_wavelength_calibrate_apply.py +0 -0
  390. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_wavelength_calibration_in_situ.py +0 -0
  391. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tests/unit/test_wavelength_calibration_solar.py +0 -0
  392. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tools/docs/__init__.py +0 -0
  393. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tools/docs/build.py +0 -0
  394. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tools/docs/generate.py +0 -0
  395. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tools/docs/generate_catalogue.py +0 -0
  396. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tools/docs/generate_cli.py +0 -0
  397. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tools/docs/generate_llms.py +0 -0
  398. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tools/docs/generate_mcp.py +0 -0
  399. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tools/docs/generate_recipes.py +0 -0
  400. {spectro_kernel-0.9.0 → spectro_kernel-0.9.1}/tools/docs/generate_validation.py +0 -0
@@ -6,6 +6,163 @@ Until `1.0.0` the public API may change between minor versions.
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.9.1] — 2026-11-09
10
+
11
+ A correction release driven by a reader's report on the public MCP server
12
+ (broken GitHub links, a `backend` field misread, one description that did
13
+ not match its code, one duplicated constant) and by the full audit it
14
+ prompted: every brick's text was compared with its code. Three kinds of
15
+ changes, kept strictly apart :
16
+
17
+ - **Kernel fixes** with no numeric effect on any brick ;
18
+ - **Behaviour corrections** on nine bricks that did not do what their
19
+ description said. None of them is used by a downstream application
20
+ (checked against NORVIS Redux, LineMill, SpecRelay and the BeSS
21
+ projects before the change) ;
22
+ - **Documentation and metadata** on forty bricks, including six used by
23
+ NORVIS Redux and LineMill, where only the text changes.
24
+
25
+ Two findings that *would* change results of bricks NORVIS Redux and
26
+ LineMill run today are deliberately **not** applied and are listed under
27
+ "Known" below for a separate decision.
28
+
29
+ ### Fixed — kernel
30
+
31
+ - `get_algorithm_source` (library, `spectro source`, MCP tool) rebuilds the
32
+ repository path from the package directory, so a wheel install reports
33
+ `src/spectro_kernel/algorithms/<category>/<file>.py` and a working GitHub
34
+ link instead of the bare file name. The public server, installed from the
35
+ wheel, returned `snr_der.py` for every brick. Tests cover the editable,
36
+ wheel and unknown layouts.
37
+ - `algorithms/_robust.py` defines `MAD_TO_SIGMA` (1.482602) and
38
+ `DER_SNR_NOISE_FACTOR` (1.482602 / √6, Stoehr et al. 2008) once ; seven
39
+ copies replaced. The three bricks that carried the rounded literal
40
+ `0.6052697` — `match_lamp_lines` 1.0.3, `reidentify_arc_features` 1.0.1,
41
+ `measure_arc_geometry` 2.0.1 — now use the exact value : a 5e-8 relative
42
+ shift of a peak-detection threshold that is never exported ; on every test
43
+ fixture the closest peak candidate sits 1.8 % from the threshold.
44
+ - The documented chain *extract → calibrate → `refine_wavelength_zeropoint`
45
+ / `measure_line_offsets`* failed after a fitted calibration, because only
46
+ the coefficient mode of `wavelength_calibrate_polynomial` cleared the
47
+ extractor's `needs_wavelength_calibration` flag (0.9.0 regression).
48
+ `wavelength_calibrate_polynomial` 1.1.1 (fit mode),
49
+ `wavelength_calibration_solar` 2.0.1 and `wavelength_calibrate_easyspec`
50
+ 1.0.2 now set `wavelength_calibrated = True` and
51
+ `needs_wavelength_calibration = False` ; the axes are unchanged.
52
+ - The documentation site builds on every push (the Netlify `ignore` script
53
+ had skipped the 0.9.0 deploy).
54
+
55
+ ### Changed — behaviour aligned on the description (bricks used by no application)
56
+
57
+ - `gaia_query` 1.1.0 — `data_release` accepts the release aliases the
58
+ description promised (`DR3`, `EDR3`, `DR2`, case-insensitive) or a full
59
+ table name ; anything else is refused instead of being pasted into the
60
+ ADQL `FROM`, where `'DR3'` made the query fail.
61
+ - `vizier_query` 2.0.0 — cone search of `radius_arcsec` around the resolved
62
+ object (`Vizier.query_region`) ; 1.x called `query_object` and ignored the
63
+ radius it documented.
64
+ - `rv_precision_bouchy` 3.0.0 — without an uncertainty array the photon
65
+ noise σ_F ∝ √|F| is scaled to the spectrum's own DER_SNR noise, as the
66
+ description always said ; `snr_floor` is only the fallback when that
67
+ estimate is unavailable (2.x always used `snr_floor`, so σ_v scaled with
68
+ an assumption, never with the data).
69
+ - `bpt_line_ratios` 3.0.0 — `log_sii_ha` needs both [S II] lines ; 2.x
70
+ reported (6716 + 6731)/Hα with one member of the doublet.
71
+ - `doppler_tomogram` 3.0.0 — velocities a spectrum does not cover stay NaN
72
+ and are excluded from the back-projection average, as the NaN-aware
73
+ description and the code comment said ; 2.x zeroed them and counted them
74
+ as phases with no excess, diluting the map.
75
+ - `merge_echelle_orders` 2.0.0 — a non-finite flux sample carries no weight ;
76
+ 1.x kept its weight in the denominator and pulled the merge toward 0
77
+ under a NaN gap.
78
+ - `read_echelle_fits` 2.0.0 — IRAF multispec `WAT2_*` records are decoded
79
+ (linear and log dispersions, `z` factor) ; a non-linear record
80
+ (Chebyshev / Legendre / spline, not decoded) leaves the order on a
81
+ flagged pixel axis (`meta["wcs"]`) instead of the shared linear WCS,
82
+ which gave a silently wrong axis. 1.x never read `WAT*` although its
83
+ docstring said so.
84
+ - `embed_pretrained` 2.1.0 — `model_path` may be a directory holding
85
+ `model.pt`, as documented.
86
+ - `plot_dynamic_spectrum` 1.1.0 — y labels are `DATE-OBS` or the exposure
87
+ index ; the object-name fallback collapsed every row of a single target
88
+ onto one label.
89
+
90
+ ### Documentation and metadata (PATCH bumps, no numeric change)
91
+
92
+ - `measure_arc_geometry` : one joint least-squares fit of every (row, dx)
93
+ sample, no per-row or per-line averaging ; the `smile_order` 4 / 6 terms
94
+ are fitted but only dy² enters R ; implemented here on scipy / numpy
95
+ primitives (the `backend = "scipy"` convention : *implemented on
96
+ numerical primitives*, not *wrapped*).
97
+ - `correct_tilt_affine` / `correct_slant_affine` / module docstring : the two
98
+ physical descriptions were swapped — tilt levels the trace (vertical
99
+ shear), slant straightens the monochromatic lines (horizontal shear) ; the
100
+ shear uses the angle in radians, ≈ tan θ for the small angles involved.
101
+ - `clip_cosmic_rays` : astroscrappy's default clean is a 5×5 masked mean,
102
+ not a local median ; `backend = "astroscrappy"`.
103
+ - `classify_template_chi2` : `top_n` includes the best match ;
104
+ `min_overlap` gates the observed spectrum (templates are dropped below
105
+ 100 shared samples) ; `teff_unstable` needs templates with a `T_eff`
106
+ (Pickles route), so it stays False and `best_teff_k` is absent on the
107
+ `atlas_dir` route.
108
+ - `extract_spectrum_easyspec` : `trace_method` is argmax / moments / multi,
109
+ `extraction_weights` gaussian / tophat, `shift_y_pixels` is the sky-window
110
+ offset raised to 3 × `trace_half_width` by easyspec ; the Monte-Carlo
111
+ errors are not kept (no uncertainty on the output).
112
+ - `cosmic_ray_remove_easyspec` : the gain correction is not optional, the
113
+ output is always in electrons.
114
+ - `wavelength_calibration_in_situ` : the uniform grid is built from the two
115
+ linear coefficients only and out-of-axis samples are filled with 0 ;
116
+ `fit_emission_lines_gaussian` writes `extras["emission_line_centroids"]`.
117
+ - `match_lamp_lines` : `wave_min` / `wave_max` declared required (the brick
118
+ already refused their absence ; `spectro preset validate` now catches it) ;
119
+ the hand-off to `wavelength_calibrate_polynomial` goes through its
120
+ parameters, as `examples/wavelength_calibration.py` shows ; peaks are the
121
+ most prominent, noise is DER_SNR ; `lamp_atlas` intensities are
122
+ informative only.
123
+ - `vr_ratio` : the most prominent peak per side, not the highest sample.
124
+ - `detect_trace` : the FWHM helper returns the span of samples above
125
+ half-maximum, no interpolation, no restriction to the centre.
126
+ - `wavelength_calibration_solar` : prominence as a fraction of the range,
127
+ matching in wavelength space.
128
+ - `subtract_sky_2d` `sky_half_width` is the window height ;
129
+ `extract_spectrum_optimal` flanking sky fit is unweighted and its lower
130
+ window starts one row further than `subtract_sky_2d`'s.
131
+ - `simbad_query`, Plotly bricks : they register without the optional
132
+ library and fail at run time with an install hint.
133
+ - `flat_combine`, `flat_normalize`, `flat_combine_easyspec` : point at
134
+ `flat_normalize_response` and `flat_normalize_easyspec`, which exist.
135
+ - `read_sdss_spectrum`, `compare_line_fits` (reports `best_profile`),
136
+ `compare_snr_methods`, `fit_telluric_scaling` (both extras keys),
137
+ `extract_spectrum_sum` (row, not column), `bias_combine` (feeds
138
+ `subtract_bias`), `_line_flux.continuum` (fitted offset),
139
+ `sii_electron_density` (fails / NaN), `export_fits_bess` (sentinel for
140
+ masked pixels only), `embed_band_power` (log10 of |power|),
141
+ `denoise_median_2d` border, `atmospheric_extinction_correct` (geometric
142
+ zenith angle), `activity_index_caii_hk` (`s_index_calibrated` is
143
+ α·S + β), `combine_spectra_arithmetic` (pass-through inside reference
144
+ NaN gaps too), `doppler_tomogram` (untimed spectra skipped, odd
145
+ `n_velocity` optional), `normalize_spline` (knots include the boundary),
146
+ embedding bricks (how to deal with masked samples),
147
+ `wavelength_calibrate_easyspec` (the applied polynomial is
148
+ `numpy.polyfit` on the identifications).
149
+ - Backend labels : `export_hdf5` h5py, `doppler_shift` numpy,
150
+ `vsini_fourier` numpy, `correct_smile_xcorr` scipy.
151
+
152
+ ### Known — not changed, for a separate decision (bricks in production)
153
+
154
+ - `wavelength_calibration_in_situ` (NORVIS Redux, LineMill) : the uniform
155
+ grid should span the polynomial over the whole axis and fill with NaN,
156
+ and the brick should clear `needs_wavelength_calibration` like the other
157
+ calibrators (a NORVIS `WAVCAL` header would flip to True).
158
+ - `correct_tilt_affine` / `correct_slant_affine` (NORVIS Redux, LineMill) :
159
+ the shear should be tan θ rather than θ (0.1 % at 3°).
160
+ - `detect_trace` (LineMill) : the FWHM should be interpolated at half-maximum
161
+ around the central row ; a second source in the slit currently widens it
162
+ and the SNR exclusion zone with it.
163
+ - `response_from_standard` (LineMill) : `n_fit_samples` and `response_rms`
164
+ reported from the post-clip mask ; to be checked against the final fit.
165
+
9
166
  ## [0.9.0] — 2026-10-23
10
167
 
11
168
  Bricks for self-calibrating long-slit instruments, requested by NORVIS Redux,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: spectro-kernel
3
- Version: 0.9.0
3
+ Version: 0.9.1
4
4
  Summary: A shared catalogue of astronomical spectroscopy algorithms, composable into reproducible pipelines, usable as a Python library, a CLI, or an MCP server.
5
5
  Project-URL: Homepage, https://github.com/matthieulel/spectro-kernel
6
6
  Project-URL: Documentation, https://docs.spectrokernel.io/
@@ -0,0 +1,29 @@
1
+ """Robust noise constants shared by the bricks (one definition, seven former copies).
2
+
3
+ ``MAD_TO_SIGMA`` converts a median absolute deviation to a Gaussian σ
4
+ (1 / Φ⁻¹(3/4) ≈ 1.4826). ``DER_SNR_NOISE_FACTOR`` is the DER_SNR noise scale
5
+ of Stoehr et al. 2008 (ASP Conf. Ser. 394, 505): σ = 1.482602 / √6 ·
6
+ median(|2 F[i] − F[i−2] − F[i+2]|).
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import math
12
+
13
+ import numpy as np
14
+
15
+ MAD_TO_SIGMA = 1.482602
16
+ DER_SNR_NOISE_FACTOR = MAD_TO_SIGMA / math.sqrt(6.0)
17
+
18
+
19
+ def der_snr_noise(flux: np.ndarray) -> float:
20
+ """DER_SNR noise estimate of a 1-D flux array (NaN-aware, 0.0 below 5 samples)."""
21
+ flux = np.asarray(flux, dtype=np.float64)
22
+ if flux.size < 5:
23
+ return 0.0
24
+ diff = np.abs(2.0 * flux[2:-2] - flux[:-4] - flux[4:])
25
+ diff = diff[np.isfinite(diff)]
26
+ return DER_SNR_NOISE_FACTOR * float(np.median(diff)) if diff.size else 0.0
27
+
28
+
29
+ __all__ = ["DER_SNR_NOISE_FACTOR", "MAD_TO_SIGMA", "der_snr_noise"]
@@ -116,7 +116,7 @@ def log_rhk_noyes(s_mw: float, b_v: float) -> float | None:
116
116
  category=AlgorithmCategory.ACTIVITY,
117
117
  # 2.0.0: masked samples (Spectrum1D.mask) are dropped from the passband
118
118
  # integrals like non-finite ones.
119
- version="2.0.0",
119
+ version="2.0.1",
120
120
  )
121
121
  class ActivityIndexCaiiHk(BaseAlgorithm):
122
122
  """Mount Wilson S index from the Ca II H & K cores, plus R'_HK when B−V is given.
@@ -162,7 +162,8 @@ class ActivityIndexCaiiHk(BaseAlgorithm):
162
162
  "calibrated on 0.44 < B−V < 0.82 : outside that range log_rhk is "
163
163
  "still written but flagged in the message. Rejects (no log_rhk) when "
164
164
  "R_HK ≤ R_phot. Metrics are prefixed for provenance: "
165
- "s_index_alpha and s_index_calibrated echo the inputs. v2.0.0: masked "
165
+ "s_index_alpha echoes the input, s_index_calibrated is the computed "
166
+ "alpha · S + beta. v2.0.0: masked "
166
167
  "samples (Spectrum1D.mask) are ignored — dropped from the passband "
167
168
  "integrals (and their transmission from the weight sum) like "
168
169
  "non-finite ones."
@@ -32,8 +32,9 @@ that suppresses the streak artefacts of pure back-projection. MEM is
32
32
  intentionally left out of v1.0.0 to keep the surface tight.
33
33
 
34
34
  Phase computation : ``φ = ((JD_mid − epoch_HJD) / period) mod 1``.
35
- Each input ``Spectrum1D`` must carry its **mid-exposure JD** in
36
- ``spec.meta["jd_mid"]`` ; if absent, the algorithm falls back to
35
+ Each input ``Spectrum1D`` should carry its **mid-exposure JD** in
36
+ ``spec.meta["jd_mid"]`` (a spectrum with no usable timestamp is skipped
37
+ and counted in the message) ; if absent, the algorithm falls back to
37
38
  deriving it from ``spec.meta["dateobs"]`` and ``spec.meta["exptime_s"]``
38
39
  (``jd_mid = Time(dateobs).jd + exptime_s / 2 / 86400``), which requires
39
40
  ``astropy.time`` (imported lazily).
@@ -85,7 +86,9 @@ def _resolve_jd_mid(spec: Spectrum1D) -> float | None:
85
86
 
86
87
 
87
88
  def _line_excess_on_velocity_grid(
88
- spec: Spectrum1D, line_centre_aa: float, velocity_grid: np.ndarray,
89
+ spec: Spectrum1D,
90
+ line_centre_aa: float,
91
+ velocity_grid: np.ndarray,
89
92
  ) -> np.ndarray | None:
90
93
  """Resample the spectrum onto a velocity grid and subtract the continuum.
91
94
 
@@ -118,9 +121,9 @@ def _line_excess_on_velocity_grid(
118
121
 
119
122
 
120
123
  def _back_project(
121
- trailed: np.ndarray, # (n_phases, n_velocity)
122
- phases: np.ndarray, # (n_phases,)
123
- velocity_grid: np.ndarray, # (n_velocity,)
124
+ trailed: np.ndarray, # (n_phases, n_velocity)
125
+ phases: np.ndarray, # (n_phases,)
126
+ velocity_grid: np.ndarray, # (n_velocity,)
124
127
  gamma_kms: float,
125
128
  n_velocity: int,
126
129
  ) -> np.ndarray:
@@ -157,7 +160,9 @@ def _back_project(
157
160
  # 2.0.0: back-projection locus follows Marsh & Horne 1988 Eq. 1
158
161
  # (v = γ − Vx·cos 2πφ + Vy·sin 2πφ); v1 had sin/cos swapped and
159
162
  # produced maps reflected with respect to the literature convention.
160
- version="2.0.0",
163
+ # 3.0.0: velocities a spectrum does not cover stay NaN and are excluded from
164
+ # the back-projection average (2.x set them to 0 and counted them).
165
+ version="3.0.0",
161
166
  )
162
167
  class DopplerTomogram(BaseAlgorithm):
163
168
  """Doppler tomogram of a binary from N phase-resolved spectra.
@@ -190,8 +195,7 @@ class DopplerTomogram(BaseAlgorithm):
190
195
 
191
196
  backend = "numpy"
192
197
  references = [
193
- "Marsh & Horne 1988, MNRAS 235, 269 — Doppler tomography by "
194
- "back-projection (§2).",
198
+ "Marsh & Horne 1988, MNRAS 235, 269 — Doppler tomography by back-projection (§2).",
195
199
  "Horne 1985, MNRAS 213, 129 — MEM tomographic reconstruction "
196
200
  "(not implemented; cited for completeness).",
197
201
  "Marsh 2001, Lecture Notes in Physics 573 — astrotomography review.",
@@ -225,7 +229,9 @@ class DopplerTomogram(BaseAlgorithm):
225
229
  "gamma_kms": "Systemic velocity (km/s) added to the back-projection.",
226
230
  "line_center_aa": "Rest wavelength (Å) of the line to tomogram.",
227
231
  "velocity_window_kms": "Half-extent (km/s) of the V_x/V_y axes.",
228
- "n_velocity": "Number of velocity samples per axis (odd, 11–401).",
232
+ "n_velocity": (
233
+ "Number of velocity samples per axis (11–401 ; an odd value puts a sample at 0)."
234
+ ),
229
235
  }
230
236
  input_requirements: list[str] = [] # validated dynamically
231
237
  output_produces = [
@@ -240,9 +246,7 @@ class DopplerTomogram(BaseAlgorithm):
240
246
  def run(self, ctx: WorkContext, params: dict[str, Any]) -> AlgorithmOutput:
241
247
  spectra = list(ctx.spectra or [])
242
248
  if len(spectra) < 3:
243
- return AlgorithmOutput.fail(
244
- f"Need ≥ 3 spectra in ctx.spectra; got {len(spectra)}."
245
- )
249
+ return AlgorithmOutput.fail(f"Need ≥ 3 spectra in ctx.spectra; got {len(spectra)}.")
246
250
 
247
251
  period = float(params["period_days"])
248
252
  if period <= 0.0:
@@ -284,11 +288,15 @@ class DopplerTomogram(BaseAlgorithm):
284
288
  rows.sort(key=lambda row: row[0])
285
289
  phases = np.asarray([row[0] for row in rows], dtype=np.float64)
286
290
  trailed = np.stack([row[1] for row in rows], axis=0)
287
- # NaN-aware: replace residual NaNs from np.interp by 0 before
288
- # accumulation (they don't contribute, and "no data" ≠ "0 signal").
289
- trailed_clean = np.where(np.isfinite(trailed), trailed, 0.0)
291
+ # NaN-aware: velocities a spectrum does not cover stay NaN; np.interp
292
+ # propagates them and _back_project excludes them from the average
293
+ # ("no data" ≠ "0 signal" — 2.x zeroed them and counted them).
290
294
  doppler_map = _back_project(
291
- trailed_clean, phases, velocity_grid, gamma_kms, n_velocity,
295
+ trailed,
296
+ phases,
297
+ velocity_grid,
298
+ gamma_kms,
299
+ n_velocity,
292
300
  )
293
301
 
294
302
  result = {
@@ -5,11 +5,35 @@ from __future__ import annotations
5
5
  from typing import Any
6
6
 
7
7
  from ...base import AlgorithmOutput, BaseAlgorithm
8
+ from ...errors import InvalidParameterError
8
9
  from ...registry import register_algorithm
9
10
  from ...types import AlgorithmCategory, CatalogResult, WorkContext
10
11
 
12
+ _RELEASE_TABLES = {
13
+ "dr3": "gaiadr3.gaia_source",
14
+ "edr3": "gaiaedr3.gaia_source",
15
+ "dr2": "gaiadr2.gaia_source",
16
+ }
11
17
 
12
- @register_algorithm("gaia_query", category=AlgorithmCategory.CATALOG, version="1.0.0")
18
+
19
+ def _release_table(value: object) -> str:
20
+ """ADQL table of a release alias ('DR3', 'EDR3', 'DR2') or a full table name."""
21
+ if value is None or str(value).strip() == "":
22
+ return _RELEASE_TABLES["dr3"]
23
+ key = str(value).strip().lower().replace("gaia", "").replace(" ", "")
24
+ if key in _RELEASE_TABLES:
25
+ return _RELEASE_TABLES[key]
26
+ if "." in str(value):
27
+ return str(value).strip()
28
+ raise InvalidParameterError(
29
+ f"data_release {value!r} is neither a release alias (DR3, EDR3, DR2) nor a "
30
+ "table name such as 'gaiadr3.gaia_source'."
31
+ )
32
+
33
+
34
+ # 1.1.0: data_release accepts the release aliases the description always
35
+ # promised (DR3, DR2, EDR3, case-insensitive) next to a full table name.
36
+ @register_algorithm("gaia_query", category=AlgorithmCategory.CATALOG, version="1.1.0")
13
37
  class GaiaQuery(BaseAlgorithm):
14
38
  """Cone-search the Gaia archive around ICRS coordinates.
15
39
 
@@ -28,9 +52,10 @@ class GaiaQuery(BaseAlgorithm):
28
52
  "Ginsburg et al. 2019, AJ 157, 98 — astroquery",
29
53
  ]
30
54
  long_description = (
31
- "Requires network access and the optional 'catalogs' extra. By default "
32
- "queries the latest release available via astroquery.gaia. Use `data_release` "
33
- "to pin a specific release such as 'DR3' or 'DR2'."
55
+ "Requires network access and the optional 'catalogs' extra. Queries "
56
+ "gaiadr3.gaia_source by default (the latest full release). `data_release` "
57
+ "accepts a release alias ('DR3', 'EDR3', 'DR2', case-insensitive) or a full "
58
+ "table name such as 'gaiadr2.gaia_source'."
34
59
  )
35
60
  default_params = {
36
61
  "ra_deg": None,
@@ -43,7 +68,10 @@ class GaiaQuery(BaseAlgorithm):
43
68
  "ra_deg": "Right ascension (ICRS) in degrees.",
44
69
  "dec_deg": "Declination (ICRS) in degrees.",
45
70
  "radius_arcsec": "Cone-search radius around the position.",
46
- "data_release": "Gaia release table to query (e.g. 'gaiadr3.gaia_source').",
71
+ "data_release": (
72
+ "Gaia release: an alias ('DR3', 'EDR3', 'DR2') or a full table name "
73
+ "('gaiadr3.gaia_source'); null means DR3."
74
+ ),
47
75
  }
48
76
  input_requirements: list[str] = []
49
77
  output_produces = ["catalog_lookups"]
@@ -53,14 +81,13 @@ class GaiaQuery(BaseAlgorithm):
53
81
  from astroquery.gaia import Gaia as _Gaia
54
82
  except ImportError:
55
83
  return AlgorithmOutput.fail(
56
- "gaia_query needs astroquery. "
57
- "Install with: pip install 'spectro-kernel[catalogs]'."
84
+ "gaia_query needs astroquery. Install with: pip install 'spectro-kernel[catalogs]'."
58
85
  )
59
86
 
60
87
  ra = float(params["ra_deg"])
61
88
  dec = float(params["dec_deg"])
62
89
  radius_deg = float(params["radius_arcsec"]) / 3600.0
63
- table_name = params.get("data_release") or "gaiadr3.gaia_source"
90
+ table_name = _release_table(params.get("data_release"))
64
91
  adql = (
65
92
  f"SELECT TOP 1 source_id, ra, dec, parallax, phot_g_mean_mag, "
66
93
  f"DISTANCE(POINT('ICRS', ra, dec), POINT('ICRS', {ra}, {dec})) AS sep "
@@ -81,7 +108,7 @@ class GaiaQuery(BaseAlgorithm):
81
108
  ctx.catalog_lookups[key] = result
82
109
  return AlgorithmOutput.ok(
83
110
  artifacts={"result": result.to_dict()},
84
- message=f"Gaia returned no source within {params['radius_arcsec']}\".",
111
+ message=f'Gaia returned no source within {params["radius_arcsec"]}".',
85
112
  )
86
113
 
87
114
  row = table[0]
@@ -95,9 +122,7 @@ class GaiaQuery(BaseAlgorithm):
95
122
  fields={
96
123
  "parallax_mas": float(row["parallax"]) if row["parallax"] is not None else None,
97
124
  "G_mag": (
98
- float(row["phot_g_mean_mag"])
99
- if row["phot_g_mean_mag"] is not None
100
- else None
125
+ float(row["phot_g_mean_mag"]) if row["phot_g_mean_mag"] is not None else None
101
126
  ),
102
127
  "separation_deg": float(row["sep"]),
103
128
  },
@@ -105,5 +130,5 @@ class GaiaQuery(BaseAlgorithm):
105
130
  ctx.catalog_lookups[source_id] = result
106
131
  return AlgorithmOutput.ok(
107
132
  artifacts={"result": result.to_dict()},
108
- message=f"Gaia source {source_id} at {row['sep']*3600:.2f}\".",
133
+ message=f'Gaia source {source_id} at {row["sep"] * 3600:.2f}".',
109
134
  )
@@ -1,8 +1,8 @@
1
1
  """Algorithm: resolve an object against the SIMBAD database.
2
2
 
3
3
  Requires the optional ``catalogs`` extra (``pip install spectro-kernel[catalogs]``).
4
- If astroquery is not installed the algorithm simply does not register, and the rest of
5
- the catalogue loads normally.
4
+ The algorithm registers whether or not astroquery is installed; without it, running
5
+ the brick fails with an install hint and the rest of the catalogue is unaffected.
6
6
  """
7
7
 
8
8
  from __future__ import annotations
@@ -25,7 +25,7 @@ def _cell(row: Any, *names: str) -> Any:
25
25
  return None
26
26
 
27
27
 
28
- @register_algorithm("simbad_query", category=AlgorithmCategory.CATALOG, version="1.0.0")
28
+ @register_algorithm("simbad_query", category=AlgorithmCategory.CATALOG, version="1.0.1")
29
29
  class SimbadQuery(BaseAlgorithm):
30
30
  """Resolve an object name against SIMBAD and store the record in the context.
31
31
 
@@ -5,11 +5,14 @@ from __future__ import annotations
5
5
  from typing import Any
6
6
 
7
7
  from ...base import AlgorithmOutput, BaseAlgorithm
8
+ from ...errors import InvalidParameterError
8
9
  from ...registry import register_algorithm
9
10
  from ...types import AlgorithmCategory, CatalogResult, WorkContext
10
11
 
11
12
 
12
- @register_algorithm("vizier_query", category=AlgorithmCategory.CATALOG, version="1.0.0")
13
+ # 2.0.0: cone search of radius_arcsec around the resolved object (query_region);
14
+ # 1.x called query_object and ignored radius_arcsec.
15
+ @register_algorithm("vizier_query", category=AlgorithmCategory.CATALOG, version="2.0.0")
13
16
  class VizierQuery(BaseAlgorithm):
14
17
  """Look up an object in VizieR — the CDS table service.
15
18
 
@@ -54,12 +57,17 @@ class VizierQuery(BaseAlgorithm):
54
57
  )
55
58
 
56
59
  name = str(params["object_name"])
60
+ if float(params["radius_arcsec"]) <= 0.0:
61
+ raise InvalidParameterError("radius_arcsec must be > 0.")
57
62
  try:
63
+ from astropy.coordinates import Angle # noqa: PLC0415
64
+
65
+ radius = Angle(float(params["radius_arcsec"]), unit="arcsec")
58
66
  v = _Vizier(row_limit=int(params["row_limit"]))
59
67
  tables = (
60
- v.query_object(name, catalog=params["catalog"])
68
+ v.query_region(name, radius=radius, catalog=params["catalog"])
61
69
  if params["catalog"]
62
- else v.query_object(name)
70
+ else v.query_region(name, radius=radius)
63
71
  )
64
72
  except Exception as exc: # noqa: BLE001 - network/parse errors
65
73
  return AlgorithmOutput.fail(f"VizieR query failed: {exc}")
@@ -15,7 +15,9 @@ top-N matches and a couple of confidence flags :
15
15
  continua).
16
16
  - ``teff_unstable`` — True when ``max − min`` of the top-3 ``T_eff``
17
17
  exceeds 500 K (the top matches span more than one effective
18
- temperature bin).
18
+ temperature bin). It needs templates with a ``T_eff``: the Pickles
19
+ atlas route provides one, templates loaded from ``atlas_dir`` do not,
20
+ so the flag stays False and ``best_teff_k`` is absent there.
19
21
 
20
22
  This is intentionally the unweighted χ² ; the kernel does not yet
21
23
  carry a per-pixel uncertainty for normalised flux. Modern alternatives
@@ -126,7 +128,7 @@ def _normalised_template(atlas: Atlas, template_id: str, window: int) -> np.ndar
126
128
  # 1.1.0: additive `atlas_dir` (path) alternative to the Python `atlas`
127
129
  # object so the brick is CLI/MCP-drivable ; per-template normalisation
128
130
  # cached on the Atlas ; descending wavelength axes sorted. Numbers unchanged.
129
- version="1.1.0",
131
+ version="1.1.1",
130
132
  )
131
133
  class ClassifyTemplateChi2(BaseAlgorithm):
132
134
  """Spectral classification by χ² against a Pickles-style template atlas.
@@ -144,7 +146,7 @@ class ClassifyTemplateChi2(BaseAlgorithm):
144
146
  Output (under ``ctx.extras["classification_report"]``) :
145
147
 
146
148
  - ``best`` : top match (``spec_type``, ``T_eff``, χ², coverage).
147
- - ``top_n`` : the next ``top_n - 1`` matches.
149
+ - ``top_n`` : the ``top_n`` best matches, the best one included.
148
150
  - ``ambiguous`` : χ²[1] / χ²[0] < 1.20.
149
151
  - ``teff_unstable`` : ``T_eff`` span across the top 3 > 500 K.
150
152
 
@@ -164,8 +166,9 @@ class ClassifyTemplateChi2(BaseAlgorithm):
164
166
  "Common wavelength grid (default 3800–7500 Å at 2 Å step, "
165
167
  "matching pickles_atlas.canonical_grid()), running-median "
166
168
  "continuum normalisation (window 80 samples), unweighted χ² over "
167
- "the samples shared by observed + template. Templates with "
168
- "useable overlap < min_overlap are skipped. Confidence flags : "
169
+ "the samples shared by observed + template. The run fails when the "
170
+ "observed spectrum covers less than min_overlap of the atlas grid ; a "
171
+ "template is dropped when fewer than 100 samples overlap. Confidence flags : "
169
172
  "ambiguous (χ²[1]/χ²[0] < 1.20) and teff_unstable (top-3 ΔT_eff "
170
173
  "> 500 K). Supply either `atlas` (an Atlas object, Python API) or "
171
174
  "`atlas_dir` (path to the Pickles .dat files, loaded with "
@@ -201,7 +204,10 @@ class ClassifyTemplateChi2(BaseAlgorithm):
201
204
  "atlas is given."
202
205
  ),
203
206
  "top_n": "Number of best-fit matches to report (≥ 1).",
204
- "min_overlap": "Reject the run if observed-vs-grid overlap drops below this fraction.",
207
+ "min_overlap": (
208
+ "Fail the run when the observed spectrum covers less than this fraction "
209
+ "of the atlas grid (templates themselves are dropped below 100 shared samples)."
210
+ ),
205
211
  "continuum_window": "Running-median window (samples) for continuum normalisation.",
206
212
  "grid_lo": "Reserved (currently unused): the atlas's own grid_aa is used.",
207
213
  "grid_hi": "Reserved (currently unused): the atlas's own grid_aa is used.",
@@ -85,7 +85,8 @@ def fit_spline_continuum(
85
85
  ) -> tuple[np.ndarray, np.ndarray, np.ndarray, int]:
86
86
  """IRAF-style iteratively clipped cubic-spline continuum.
87
87
 
88
- Returns ``(continuum, kept_mask, knots, n_iterations)`` with the
88
+ Returns ``(continuum, kept_mask, knots, n_iterations)`` — ``knots`` are
89
+ the knots actually used, boundary knots included — with the
89
90
  continuum evaluated on every ``x``. ``low_reject`` / ``high_reject``
90
91
  ≤ 0 disable rejection on that side, as in IRAF.
91
92
  """
@@ -137,7 +138,7 @@ def fit_spline_continuum(
137
138
  # 2.0.0: masked samples (Spectrum1D.mask) are excluded from the spline fit
138
139
  # and from continuum_rms / n_rejected ; the output keeps their flux,
139
140
  # divided like every other sample, and carries the mask through.
140
- version="2.0.0",
141
+ version="2.0.1",
141
142
  )
142
143
  class NormalizeSpline(BaseAlgorithm):
143
144
  """Normalise the continuum with an IRAF-style iteratively clipped cubic spline.
@@ -22,8 +22,8 @@ user table) is always preferable.
22
22
  The airmass comes from, in order : the ``airmass`` parameter ; the
23
23
  ``AIRMASS`` FITS keyword ; or the geometry (target RA / DEC, DATE-OBS at
24
24
  mid-exposure, observer site) through astropy ``AltAz`` and the
25
- Kasten & Young 1989 formula (their Eq. 3, z the apparent zenith
26
- angle in degrees) :
25
+ Kasten & Young 1989 formula (their Eq. 3, z the zenith angle in
26
+ degrees — geometric here, ``AltAz`` being evaluated without refraction) :
27
27
 
28
28
  X(z) = 1 / [cos z + 0.50572 · (96.07995 − z)^{−1.6364}] (Eq. 2)
29
29
 
@@ -287,7 +287,7 @@ def _airmass_from_geometry(
287
287
  @register_algorithm(
288
288
  "atmospheric_extinction_correct",
289
289
  category=AlgorithmCategory.CORRECTION,
290
- version="1.0.0",
290
+ version="1.0.1",
291
291
  )
292
292
  class AtmosphericExtinctionCorrect(BaseAlgorithm):
293
293
  """Remove atmospheric extinction : F_0 = F · 10^(0.4 · k(λ) · X) with a mean site curve.
@@ -14,7 +14,7 @@ from ...types import AlgorithmCategory, WorkContext
14
14
  _C_KMS = c.to(u.km / u.s).value
15
15
 
16
16
 
17
- @register_algorithm("doppler_shift", category=AlgorithmCategory.CORRECTION, version="1.0.0")
17
+ @register_algorithm("doppler_shift", category=AlgorithmCategory.CORRECTION, version="1.0.1")
18
18
  class DopplerShift(BaseAlgorithm):
19
19
  """Doppler-shift the wavelength axis by a radial velocity.
20
20
 
@@ -23,7 +23,7 @@ class DopplerShift(BaseAlgorithm):
23
23
  ``-v``. The flux array is unchanged.
24
24
  """
25
25
 
26
- backend = "astropy"
26
+ backend = "numpy"
27
27
  references = [
28
28
  "Classical (non-relativistic) Doppler relation: lambda' = lambda * (1 + v/c)",
29
29
  "Speed of light from astropy.constants",
@@ -16,7 +16,7 @@ from ...types import AlgorithmCategory, Spectrum1D, WorkContext
16
16
  @register_algorithm(
17
17
  "fit_telluric_scaling",
18
18
  category=AlgorithmCategory.CORRECTION,
19
- version="1.0.1",
19
+ version="1.0.2",
20
20
  )
21
21
  class FitTelluricScaling(BaseAlgorithm):
22
22
  """Fit the airmass that best matches a telluric template to ``ctx.spectrum``.
@@ -25,7 +25,8 @@ class FitTelluricScaling(BaseAlgorithm):
25
25
  directly) and an observed spectrum, this finds the airmass exponent
26
26
  ``a*`` minimising the residual depth of telluric bands after dividing —
27
27
  Beer-Lambert says ``T_obs = T_template^a``. The fitted airmass plus the
28
- rescaled template land in ``ctx.extras['telluric_fit']``; chain into
28
+ rescaled template land in ``ctx.extras['telluric_fit']`` and
29
+ ``ctx.extras['telluric_template_scaled']``; chain into
29
30
  ``remove_telluric_division`` to actually apply the correction.
30
31
  """
31
32
 
@@ -24,7 +24,7 @@ from ...types import AlgorithmCategory, WorkContext
24
24
  # 2.0.0: half-open band edges (no double-counted samples)
25
25
  # 2.0.1: non-finite samples dropped ; descending axes sorted before binning.
26
26
  # 3.0.0: masked samples (Spectrum1D.mask) are dropped like non-finite ones.
27
- version="3.0.0",
27
+ version="3.0.1",
28
28
  )
29
29
  class EmbedBandPower(BaseAlgorithm):
30
30
  """Embed a spectrum as the integrated flux in N adjacent wavelength bands.
@@ -68,7 +68,7 @@ class EmbedBandPower(BaseAlgorithm):
68
68
  param_descriptions = {
69
69
  "n_bands": "Number of bands; also the output dim. Typically 8–32.",
70
70
  "spacing": "Band edges in wavelength: 'linear' or 'log'.",
71
- "log_flux": "Take log10(integrated_flux + epsilon) before L2 norm.",
71
+ "log_flux": "Take log10(|integrated_flux| + epsilon) before L2 norm.",
72
72
  "epsilon": "Small floor added before log_flux to keep zero bands finite.",
73
73
  }
74
74
  input_requirements = ["spectrum"]