hbtsim 0.2.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 (202) hide show
  1. hbtsim-0.2.0/LICENSE +28 -0
  2. hbtsim-0.2.0/PKG-INFO +635 -0
  3. hbtsim-0.2.0/README.md +569 -0
  4. hbtsim-0.2.0/hbtsim/__init__.py +27 -0
  5. hbtsim-0.2.0/hbtsim/__main__.py +35 -0
  6. hbtsim-0.2.0/hbtsim/aperture.py +315 -0
  7. hbtsim-0.2.0/hbtsim/bispectrum.py +396 -0
  8. hbtsim-0.2.0/hbtsim/catalog/__init__.py +285 -0
  9. hbtsim-0.2.0/hbtsim/catalog/build.py +347 -0
  10. hbtsim-0.2.0/hbtsim/catalog/cli.py +90 -0
  11. hbtsim-0.2.0/hbtsim/catalog/registry.py +113 -0
  12. hbtsim-0.2.0/hbtsim/catalog/schema.py +389 -0
  13. hbtsim-0.2.0/hbtsim/chromatic.py +289 -0
  14. hbtsim-0.2.0/hbtsim/cli.py +44 -0
  15. hbtsim-0.2.0/hbtsim/cli_common.py +143 -0
  16. hbtsim-0.2.0/hbtsim/configs/arrays/c2pu_pair.json +21 -0
  17. hbtsim-0.2.0/hbtsim/configs/arrays/eonsii_pair_teide.json +23 -0
  18. hbtsim-0.2.0/hbtsim/configs/arrays/eonsii_triangle_paranal.json +24 -0
  19. hbtsim-0.2.0/hbtsim/configs/arrays/keck_pair.json +26 -0
  20. hbtsim-0.2.0/hbtsim/configs/arrays/lpqi_orm.json +57 -0
  21. hbtsim-0.2.0/hbtsim/configs/arrays/lpqi_pathfinder.json +29 -0
  22. hbtsim-0.2.0/hbtsim/configs/arrays/magic_lst1.json +39 -0
  23. hbtsim-0.2.0/hbtsim/configs/arrays/maunakea_subaru_keck.json +33 -0
  24. hbtsim-0.2.0/hbtsim/configs/arrays/veritas.json +43 -0
  25. hbtsim-0.2.0/hbtsim/configs/arrays/vlt_ut.json +44 -0
  26. hbtsim-0.2.0/hbtsim/configs/backends/eonsii_mcp.json +11 -0
  27. hbtsim-0.2.0/hbtsim/configs/backends/eonsii_mcp_pbs.json +11 -0
  28. hbtsim-0.2.0/hbtsim/configs/backends/eonsii_r7500_spad.json +11 -0
  29. hbtsim-0.2.0/hbtsim/configs/backends/eonsii_spad.json +11 -0
  30. hbtsim-0.2.0/hbtsim/configs/backends/eonsii_spad_pbs.json +11 -0
  31. hbtsim-0.2.0/hbtsim/configs/backends/kk_1000ch.json +11 -0
  32. hbtsim-0.2.0/hbtsim/configs/backends/lpqi_500.json +13 -0
  33. hbtsim-0.2.0/hbtsim/configs/backends/lpqi_550.json +13 -0
  34. hbtsim-0.2.0/hbtsim/configs/backends/lpqi_halpha.json +13 -0
  35. hbtsim-0.2.0/hbtsim/configs/backends/lpqi_hbeta.json +13 -0
  36. hbtsim-0.2.0/hbtsim/configs/backends/lpqi_nextgen_500.json +13 -0
  37. hbtsim-0.2.0/hbtsim/configs/backends/lpqi_nextgen_550.json +13 -0
  38. hbtsim-0.2.0/hbtsim/configs/backends/lpqi_nextgen_halpha.json +13 -0
  39. hbtsim-0.2.0/hbtsim/configs/backends/lpqi_nextgen_hbeta.json +13 -0
  40. hbtsim-0.2.0/hbtsim/configs/backends/magic_sii.json +20 -0
  41. hbtsim-0.2.0/hbtsim/configs/backends/r5000_correlator.json +11 -0
  42. hbtsim-0.2.0/hbtsim/configs/backends/r5000_correlator_pbs.json +11 -0
  43. hbtsim-0.2.0/hbtsim/configs/backends/spad320_correlator.json +11 -0
  44. hbtsim-0.2.0/hbtsim/configs/backends/spad320_timetag.json +11 -0
  45. hbtsim-0.2.0/hbtsim/configs/backends/veritas_sii.json +35 -0
  46. hbtsim-0.2.0/hbtsim/configs/bands/photometric.json +55 -0
  47. hbtsim-0.2.0/hbtsim/configs/campaigns/chromatic_c2pu_regression.json +40 -0
  48. hbtsim-0.2.0/hbtsim/configs/campaigns/chromatic_sirius_vega_eonsii.json +39 -0
  49. hbtsim-0.2.0/hbtsim/configs/campaigns/g2_binaries_lpqi_pathfinder.json +45 -0
  50. hbtsim-0.2.0/hbtsim/configs/campaigns/g2_c2pu.json +39 -0
  51. hbtsim-0.2.0/hbtsim/configs/campaigns/g2_eonsii.json +40 -0
  52. hbtsim-0.2.0/hbtsim/configs/campaigns/g2_keck.json +38 -0
  53. hbtsim-0.2.0/hbtsim/configs/campaigns/g3_algol_maunakea.json +38 -0
  54. hbtsim-0.2.0/hbtsim/configs/campaigns/g3_betaaur_maunakea.json +36 -0
  55. hbtsim-0.2.0/hbtsim/configs/campaigns/g3_deltavel_eonsii_paranal.json +54 -0
  56. hbtsim-0.2.0/hbtsim/configs/campaigns/g3_deltavel_vlt.json +36 -0
  57. hbtsim-0.2.0/hbtsim/configs/campaigns/g3_spica_vlt.json +36 -0
  58. hbtsim-0.2.0/hbtsim/configs/campaigns/mc_sirius_b_eonsii.json +56 -0
  59. hbtsim-0.2.0/hbtsim/configs/campaigns/mc_sirius_b_lpqi_pathfinder.json +42 -0
  60. hbtsim-0.2.0/hbtsim/configs/campaigns/redclump_ii_dwarf.json +56 -0
  61. hbtsim-0.2.0/hbtsim/configs/campaigns/redclump_ii_supergiant.json +56 -0
  62. hbtsim-0.2.0/hbtsim/configs/campaigns/suite_phase6.json +92 -0
  63. hbtsim-0.2.0/hbtsim/configs/detectors/eonsii_mcp_pmt.json +41 -0
  64. hbtsim-0.2.0/hbtsim/configs/detectors/eonsii_spad.json +43 -0
  65. hbtsim-0.2.0/hbtsim/configs/detectors/eonsii_spad_correlator.json +13 -0
  66. hbtsim-0.2.0/hbtsim/configs/detectors/kk_ideal.json +27 -0
  67. hbtsim-0.2.0/hbtsim/configs/detectors/lpqi_spad64_i2cass.json +39 -0
  68. hbtsim-0.2.0/hbtsim/configs/detectors/lpqi_spad_nextgen.json +73 -0
  69. hbtsim-0.2.0/hbtsim/configs/detectors/magic_pmt.json +27 -0
  70. hbtsim-0.2.0/hbtsim/configs/detectors/spad_lambda.json +75 -0
  71. hbtsim-0.2.0/hbtsim/configs/detectors/spad_lambda_ng.json +13 -0
  72. hbtsim-0.2.0/hbtsim/configs/detectors/veritas_pmt.json +27 -0
  73. hbtsim-0.2.0/hbtsim/configs/ld_tables/claret11_algol_a.json +45 -0
  74. hbtsim-0.2.0/hbtsim/configs/ld_tables/claret11_algol_b.json +45 -0
  75. hbtsim-0.2.0/hbtsim/configs/ld_tables/claret11_betaaur.json +35 -0
  76. hbtsim-0.2.0/hbtsim/configs/ld_tables/claret11_spica.json +39 -0
  77. hbtsim-0.2.0/hbtsim/configs/ld_tables/k_giant_rough.json +47 -0
  78. hbtsim-0.2.0/hbtsim/configs/resources/newera.json +20 -0
  79. hbtsim-0.2.0/hbtsim/configs/resources/newera_redclump.json +19 -0
  80. hbtsim-0.2.0/hbtsim/configs/sites/calern.json +15 -0
  81. hbtsim-0.2.0/hbtsim/configs/sites/flwo.json +11 -0
  82. hbtsim-0.2.0/hbtsim/configs/sites/maunakea.json +11 -0
  83. hbtsim-0.2.0/hbtsim/configs/sites/orm.json +11 -0
  84. hbtsim-0.2.0/hbtsim/configs/sites/orm_not.json +15 -0
  85. hbtsim-0.2.0/hbtsim/configs/sites/paranal.json +13 -0
  86. hbtsim-0.2.0/hbtsim/configs/sites/teide.json +13 -0
  87. hbtsim-0.2.0/hbtsim/configs/spectrographs/eonsii_1000ch.json +16 -0
  88. hbtsim-0.2.0/hbtsim/configs/spectrographs/eonsii_60ch.json +14 -0
  89. hbtsim-0.2.0/hbtsim/configs/spectrographs/eonsii_r7500.json +16 -0
  90. hbtsim-0.2.0/hbtsim/configs/spectrographs/filter_2mass_h.json +16 -0
  91. hbtsim-0.2.0/hbtsim/configs/spectrographs/filter_2mass_k.json +16 -0
  92. hbtsim-0.2.0/hbtsim/configs/spectrographs/filter_cousins_i.json +16 -0
  93. hbtsim-0.2.0/hbtsim/configs/spectrographs/filter_cousins_r.json +16 -0
  94. hbtsim-0.2.0/hbtsim/configs/spectrographs/filter_johnson_b.json +16 -0
  95. hbtsim-0.2.0/hbtsim/configs/spectrographs/filter_johnson_v.json +16 -0
  96. hbtsim-0.2.0/hbtsim/configs/spectrographs/filter_lpqi_500_1nm.json +21 -0
  97. hbtsim-0.2.0/hbtsim/configs/spectrographs/filter_lpqi_550_1nm.json +21 -0
  98. hbtsim-0.2.0/hbtsim/configs/spectrographs/filter_lpqi_halpha_1nm.json +21 -0
  99. hbtsim-0.2.0/hbtsim/configs/spectrographs/filter_lpqi_hbeta_1nm.json +21 -0
  100. hbtsim-0.2.0/hbtsim/configs/spectrographs/hbeta_window_r5000.json +14 -0
  101. hbtsim-0.2.0/hbtsim/configs/spectrographs/kk_1000ch_400_950.json +14 -0
  102. hbtsim-0.2.0/hbtsim/configs/spectrographs/r5000_400_950.json +14 -0
  103. hbtsim-0.2.0/hbtsim/configs/spectrographs/spad_lambda_320.json +16 -0
  104. hbtsim-0.2.0/hbtsim/configs/targets/40eri_b.json +34 -0
  105. hbtsim-0.2.0/hbtsim/configs/targets/algol.json +72 -0
  106. hbtsim-0.2.0/hbtsim/configs/targets/betaaur.json +72 -0
  107. hbtsim-0.2.0/hbtsim/configs/targets/deltavel.json +72 -0
  108. hbtsim-0.2.0/hbtsim/configs/targets/gammacas.json +46 -0
  109. hbtsim-0.2.0/hbtsim/configs/targets/hd17652.json +46 -0
  110. hbtsim-0.2.0/hbtsim/configs/targets/hd360.json +46 -0
  111. hbtsim-0.2.0/hbtsim/configs/targets/procyon_b.json +29 -0
  112. hbtsim-0.2.0/hbtsim/configs/targets/sirius_a.json +32 -0
  113. hbtsim-0.2.0/hbtsim/configs/targets/sirius_b.json +37 -0
  114. hbtsim-0.2.0/hbtsim/configs/targets/spica.json +71 -0
  115. hbtsim-0.2.0/hbtsim/configs/targets/vega.json +30 -0
  116. hbtsim-0.2.0/hbtsim/configs/telescopes/c2pu_1m.json +12 -0
  117. hbtsim-0.2.0/hbtsim/configs/telescopes/eonsii_4m.json +18 -0
  118. hbtsim-0.2.0/hbtsim/configs/telescopes/gtc_10p4m.json +19 -0
  119. hbtsim-0.2.0/hbtsim/configs/telescopes/int_2p54m.json +17 -0
  120. hbtsim-0.2.0/hbtsim/configs/telescopes/keck_10m.json +12 -0
  121. hbtsim-0.2.0/hbtsim/configs/telescopes/kk_4m.json +14 -0
  122. hbtsim-0.2.0/hbtsim/configs/telescopes/lst1_23m.json +17 -0
  123. hbtsim-0.2.0/hbtsim/configs/telescopes/magic_17m.json +13 -0
  124. hbtsim-0.2.0/hbtsim/configs/telescopes/not_2p56m.json +17 -0
  125. hbtsim-0.2.0/hbtsim/configs/telescopes/subaru_8p2m.json +12 -0
  126. hbtsim-0.2.0/hbtsim/configs/telescopes/tng_3p58m.json +17 -0
  127. hbtsim-0.2.0/hbtsim/configs/telescopes/veritas_12m.json +18 -0
  128. hbtsim-0.2.0/hbtsim/configs/telescopes/vlt_ut_8p2m.json +10 -0
  129. hbtsim-0.2.0/hbtsim/configs/telescopes/wht_4p2m.json +18 -0
  130. hbtsim-0.2.0/hbtsim/data.py +382 -0
  131. hbtsim-0.2.0/hbtsim/diameter.py +104 -0
  132. hbtsim-0.2.0/hbtsim/estimators.py +107 -0
  133. hbtsim-0.2.0/hbtsim/fftmap.py +105 -0
  134. hbtsim-0.2.0/hbtsim/g2spec.py +297 -0
  135. hbtsim-0.2.0/hbtsim/geometry.py +111 -0
  136. hbtsim-0.2.0/hbtsim/hbt.py +278 -0
  137. hbtsim-0.2.0/hbtsim/iact.py +210 -0
  138. hbtsim-0.2.0/hbtsim/limbdark.py +194 -0
  139. hbtsim-0.2.0/hbtsim/montecarlo.py +370 -0
  140. hbtsim-0.2.0/hbtsim/movie.py +272 -0
  141. hbtsim-0.2.0/hbtsim/orbit.py +162 -0
  142. hbtsim-0.2.0/hbtsim/params.py +450 -0
  143. hbtsim-0.2.0/hbtsim/photometry.py +102 -0
  144. hbtsim-0.2.0/hbtsim/render.py +168 -0
  145. hbtsim-0.2.0/hbtsim/run_campaign.py +233 -0
  146. hbtsim-0.2.0/hbtsim/runners/__init__.py +30 -0
  147. hbtsim-0.2.0/hbtsim/runners/chromatic.py +215 -0
  148. hbtsim-0.2.0/hbtsim/runners/g2.py +301 -0
  149. hbtsim-0.2.0/hbtsim/runners/g3.py +359 -0
  150. hbtsim-0.2.0/hbtsim/runners/g3_campaign.py +334 -0
  151. hbtsim-0.2.0/hbtsim/runners/montecarlo.py +195 -0
  152. hbtsim-0.2.0/hbtsim/runners/scale.py +253 -0
  153. hbtsim-0.2.0/hbtsim/runners/suite.py +70 -0
  154. hbtsim-0.2.0/hbtsim/sed.py +698 -0
  155. hbtsim-0.2.0/hbtsim/serialize.py +87 -0
  156. hbtsim-0.2.0/hbtsim/single.py +284 -0
  157. hbtsim-0.2.0/hbtsim/snr.py +629 -0
  158. hbtsim-0.2.0/hbtsim/snr3.py +805 -0
  159. hbtsim-0.2.0/hbtsim/snr_cli.py +191 -0
  160. hbtsim-0.2.0/hbtsim/spectral.py +148 -0
  161. hbtsim-0.2.0/hbtsim.egg-info/PKG-INFO +635 -0
  162. hbtsim-0.2.0/hbtsim.egg-info/SOURCES.txt +200 -0
  163. hbtsim-0.2.0/hbtsim.egg-info/dependency_links.txt +1 -0
  164. hbtsim-0.2.0/hbtsim.egg-info/entry_points.txt +8 -0
  165. hbtsim-0.2.0/hbtsim.egg-info/requires.txt +16 -0
  166. hbtsim-0.2.0/hbtsim.egg-info/top_level.txt +1 -0
  167. hbtsim-0.2.0/pyproject.toml +65 -0
  168. hbtsim-0.2.0/setup.cfg +4 -0
  169. hbtsim-0.2.0/tests/test_algol.py +132 -0
  170. hbtsim-0.2.0/tests/test_aperture.py +264 -0
  171. hbtsim-0.2.0/tests/test_bispectrum.py +141 -0
  172. hbtsim-0.2.0/tests/test_campaign.py +155 -0
  173. hbtsim-0.2.0/tests/test_catalog.py +284 -0
  174. hbtsim-0.2.0/tests/test_catalog_values.py +117 -0
  175. hbtsim-0.2.0/tests/test_chromatic.py +94 -0
  176. hbtsim-0.2.0/tests/test_cli_catalog.py +82 -0
  177. hbtsim-0.2.0/tests/test_data.py +173 -0
  178. hbtsim-0.2.0/tests/test_deltavel.py +110 -0
  179. hbtsim-0.2.0/tests/test_dft_core.py +151 -0
  180. hbtsim-0.2.0/tests/test_diameter.py +44 -0
  181. hbtsim-0.2.0/tests/test_docs_tables.py +13 -0
  182. hbtsim-0.2.0/tests/test_eonsii.py +56 -0
  183. hbtsim-0.2.0/tests/test_estimators.py +65 -0
  184. hbtsim-0.2.0/tests/test_fftmap.py +75 -0
  185. hbtsim-0.2.0/tests/test_geometry.py +180 -0
  186. hbtsim-0.2.0/tests/test_guards.py +90 -0
  187. hbtsim-0.2.0/tests/test_hygiene.py +133 -0
  188. hbtsim-0.2.0/tests/test_iact.py +67 -0
  189. hbtsim-0.2.0/tests/test_montecarlo.py +108 -0
  190. hbtsim-0.2.0/tests/test_newera.py +416 -0
  191. hbtsim-0.2.0/tests/test_physics_hooks.py +119 -0
  192. hbtsim-0.2.0/tests/test_runner_campaign_mc.py +138 -0
  193. hbtsim-0.2.0/tests/test_runner_chromatic_scale.py +170 -0
  194. hbtsim-0.2.0/tests/test_runner_g3_g2.py +172 -0
  195. hbtsim-0.2.0/tests/test_runner_suite.py +37 -0
  196. hbtsim-0.2.0/tests/test_sanity.py +194 -0
  197. hbtsim-0.2.0/tests/test_sed.py +262 -0
  198. hbtsim-0.2.0/tests/test_single.py +89 -0
  199. hbtsim-0.2.0/tests/test_snr.py +308 -0
  200. hbtsim-0.2.0/tests/test_snr3.py +253 -0
  201. hbtsim-0.2.0/tests/test_spectral.py +143 -0
  202. hbtsim-0.2.0/tests/test_spica.py +45 -0
hbtsim-0.2.0/LICENSE ADDED
@@ -0,0 +1,28 @@
1
+ BSD 3-Clause License
2
+
3
+ Copyright (c) 2026, Peter Nugent
4
+
5
+ Redistribution and use in source and binary forms, with or without
6
+ modification, are permitted provided that the following conditions are met:
7
+
8
+ 1. Redistributions of source code must retain the above copyright notice, this
9
+ list of conditions and the following disclaimer.
10
+
11
+ 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ this list of conditions and the following disclaimer in the documentation
13
+ and/or other materials provided with the distribution.
14
+
15
+ 3. Neither the name of the copyright holder nor the names of its
16
+ contributors may be used to endorse or promote products derived from
17
+ this software without specific prior written permission.
18
+
19
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
hbtsim-0.2.0/PKG-INFO ADDED
@@ -0,0 +1,635 @@
1
+ Metadata-Version: 2.4
2
+ Name: hbtsim
3
+ Version: 0.2.0
4
+ Summary: Photon-counting intensity interferometry (Hanbury Brown-Twiss) of bright binary and single stars: visibilities, closure phases, photon budgets, campaigns
5
+ Author-email: Peter Nugent <penugent@lbl.gov>
6
+ License: BSD 3-Clause License
7
+
8
+ Copyright (c) 2026, Peter Nugent
9
+
10
+ Redistribution and use in source and binary forms, with or without
11
+ modification, are permitted provided that the following conditions are met:
12
+
13
+ 1. Redistributions of source code must retain the above copyright notice, this
14
+ list of conditions and the following disclaimer.
15
+
16
+ 2. Redistributions in binary form must reproduce the above copyright notice,
17
+ this list of conditions and the following disclaimer in the documentation
18
+ and/or other materials provided with the distribution.
19
+
20
+ 3. Neither the name of the copyright holder nor the names of its
21
+ contributors may be used to endorse or promote products derived from
22
+ this software without specific prior written permission.
23
+
24
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
25
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
27
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
28
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
30
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
31
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
32
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
33
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34
+
35
+ Project-URL: Homepage, https://github.com/nugent68/hbtsim
36
+ Project-URL: Source, https://github.com/nugent68/hbtsim
37
+ Project-URL: Issues, https://github.com/nugent68/hbtsim/issues
38
+ Project-URL: Documentation, https://github.com/nugent68/hbtsim/tree/main/docs
39
+ Keywords: intensity interferometry,Hanbury Brown-Twiss,binary stars,closure phase,astronomy,JAX
40
+ Classifier: Development Status :: 4 - Beta
41
+ Classifier: Intended Audience :: Science/Research
42
+ Classifier: License :: OSI Approved :: BSD License
43
+ Classifier: Operating System :: OS Independent
44
+ Classifier: Programming Language :: Python :: 3
45
+ Classifier: Programming Language :: Python :: 3.11
46
+ Classifier: Programming Language :: Python :: 3.12
47
+ Classifier: Programming Language :: Python :: 3.13
48
+ Classifier: Topic :: Scientific/Engineering :: Astronomy
49
+ Classifier: Topic :: Scientific/Engineering :: Physics
50
+ Requires-Python: >=3.11
51
+ Description-Content-Type: text/markdown
52
+ License-File: LICENSE
53
+ Requires-Dist: jax>=0.4.32
54
+ Requires-Dist: numpy>=2.0
55
+ Requires-Dist: scipy>=1.12
56
+ Provides-Extra: movie
57
+ Requires-Dist: matplotlib>=3.8; extra == "movie"
58
+ Provides-Extra: test
59
+ Requires-Dist: pytest>=8.0; extra == "test"
60
+ Requires-Dist: matplotlib>=3.8; extra == "test"
61
+ Provides-Extra: gpu
62
+ Requires-Dist: jax[cuda12]>=0.4.32; extra == "gpu"
63
+ Provides-Extra: sed
64
+ Requires-Dist: h5py>=3.10; extra == "sed"
65
+ Dynamic: license-file
66
+
67
+ # hbtsim — Intensity interferometry of binary stars
68
+
69
+ Simulation of the Hanbury Brown–Twiss (HBT) effect in optical intensity
70
+ interferometry for bright binaries: **Beta Aurigae (Menkalinan)**,
71
+ **Algol (β Persei) A–B**, **Spica** and **δ Velorum Aa–Ab**. The code renders each binary
72
+ as a pair of limb-darkened stellar disks on a sky grid, computes the
73
+ complex visibility V(**u**) by an exact discrete Fourier transform of the
74
+ image at the sampled baselines (JAX, GPU-batched for spectral work),
75
+ predicts the signal-to-noise of real photon-counting
76
+ observations, and produces movies:
77
+
78
+ 1. **Orbit movie** (`hbtsim movie --target {betaaur,algol,spica,deltavel}`) — three
79
+ panels over one orbital period: the stars on the sky (false-color RGB
80
+ from renders at 700/550/440 nm, Planck-weighted and white-balanced to
81
+ 7500 K, so hot stars look blue and cool ones orange; to-scale mas
82
+ axes), the apparent g/i lightcurves anchored to observed photometry,
83
+ and g²(B) at 400 and 800 nm for baselines 10–150 m.
84
+ 2. **g²(λ) measurement movie** (`hbtsim g2spec`) — the stars on
85
+ the sky beside the g² *spectrum* a real telescope pair would measure
86
+ each hour: 320 spectral channels (400–950 nm) with 1σ error bars from
87
+ the full photon budget, simulated noisy data over the true curve.
88
+
89
+ All simulations start at greatest projected separation (quadrature) and
90
+ follow the baseline along the projected separation axis by default
91
+ (`--baseline-pa` for a fixed instrumental orientation).
92
+
93
+ ## The four systems
94
+
95
+ <!-- catalog:systems -->
96
+ | | **Beta Aurigae** (`betaaur`) | **Algol A–B** (`algol`) | **Spica** (`spica`) | **δ Velorum Aa–Ab** (`deltavel`) |
97
+ |---|---|---|---|---|
98
+ | Components | A1m IV + A1m IV near-twins | B8V dwarf + K0IV subgiant | B1 III-IV + B2 V | A2 IV + A4 V rapid rotators |
99
+ | Masses | 2.376 / 2.291 M☉ | 3.17 / 0.7 M☉ | 11.43 / 7.21 M☉ | 2.43 / 2.27 M☉ |
100
+ | Radii | 2.762 / 2.568 R☉ | 2.73 / 3.48 R☉ | 7.47 / 3.74 R☉ | 2.97 / 2.52 R☉ |
101
+ | T_eff | 9350 / 9200 K | 12550 / 4900 K | 25300 / 20900 K | 9450 / 9830 K |
102
+ | Period | 3.96004 d | 2.867328 d | 4.0145 d | 45.1503 d, e = 0.29 |
103
+ | Inclination | 76.8° | 98.7° | 63.1° | 89.0° |
104
+ | Distance | 24.87 pc | 28.82 pc | 76.6 pc | 25.13 pc (orbital parallax) |
105
+ | Angular semi-major axis | 3.29 mas | 2.15 mas | 1.71 mas | 16.54 mas |
106
+ | Angular diameters | 1.03 / 0.96 mas | 0.88 / 1.12 mas | 0.91 / 0.45 mas | 1.10 / 0.93 mas |
107
+ | Ω (ascending node) | 295.15° | 43.43° | 131.6° | 65° |
108
+ | Eclipses | partial, ~0.08 mag | deep primary, **1.48 mag in g** | none | grazing |
109
+ | Anchored photometry | g 1.80, i 2.10 | g 2.07, i 2.58 (C-corrected) | g 0.71, i 1.06 | g 1.90, i 2.25 (B-corrected) |
110
+ | NewEra tables | both stars (interpolated) | A (clamped to 12 000 K); B blackbody | blackbody | both stars (interpolated) |
111
+ | Sources | Southworth et al. 2007; Hipparcos; Jonak et al. 2026 (Ω) | Baron et al. 2012 (CHARA); Zavala et al. 2010; Kolbas et al. 2015 | Herbison-Evans et al. 1971; Tkachenko et al. 2016 | Mérand et al. 2011 |
112
+ <!-- /catalog -->
113
+
114
+ The two systems probe complementary regimes. Beta Aurigae is the clean
115
+ textbook case: equal stars, equal colors, fringes of period λ/ρ ≈ 25 m
116
+ (400 nm) modulating the disk envelopes. Algol is the extreme-contrast
117
+ case: the hot dwarf supplies nearly all the blue light while the cool
118
+ subgiant matters only in the red, so g²(B, λ) is strongly
119
+ wavelength-dependent — nearly a single-star envelope at 400 nm but with
120
+ clear binary fringes at 800 nm — and the primary eclipse (the orange
121
+ subgiant covering ~75% of the blue dwarf's area) plunges the system by
122
+ 1.5 mag while transforming the g² spectrum. Algol is also why limb
123
+ darkening is **per star** (`Star.ld_table_nm`, Claret & Bloemen 2011
124
+ linear coefficients): u(400 nm) = 0.42 for the B8V primary vs 0.89 for
125
+ the K subgiant.
126
+
127
+ Algol caveats: component C (~70 mas away) is excluded — the published
128
+ V = 2.12 and 1.27 mag eclipse depth include its ~10% third light, so the
129
+ model anchors and validation use C-corrected values (undiluted depth
130
+ ≈ 1.5 mag, matched by the model's 1.48). The Roche-lobe-filling
131
+ secondary is rendered as a sphere, so the ellipsoidal variation and
132
+ reflection effect of the real out-of-eclipse lightcurve are absent.
133
+
134
+ **Spica** (α Vir, V = 0.97; Herbison-Evans et al. 1971 Narrabri
135
+ intensity-interferometer orbit, Tkachenko et al. 2016 disentangling) is
136
+ the recommended bright target for three-telescope closure-phase work
137
+ (true e = 0.108 approximated as circular; β Cep pulsations and tidal
138
+ distortion not modeled; non-eclipsing). **δ Vel** (Mérand et al. 2011:
139
+ eccentric 45-day orbit, both components rotating at ~145 km/s, not
140
+ modeled as oblate) sits at 25.1 pc, so its 16.6 mas orbit puts the blue
141
+ fringe period (4.7 m) below 8 m pupils: it is a target for 1–4 m
142
+ telescopes, not for the VLT. Its NewEra photometry comes out 0.24 mag
143
+ brighter than the observed A-only V, a tension in the published
144
+ parameters recorded in `tests/test_newera.py`.
145
+
146
+ Every target, telescope, detector, spectrograph, backend, site, array and
147
+ campaign is a JSON file under `hbtsim/configs/<kind>s/` (the table above
148
+ is rendered from them by `scripts/catalog_tables.py`). Adding a binary
149
+ is one file: copy `hbtsim/configs/targets/betaaur.json` to
150
+ `my_target.json` in a directory of your own, edit the numbers and the
151
+ `sources` / `notes`, and point the tools at it:
152
+
153
+ ```bash
154
+ export HBTSIM_CONFIG_PATH=/path/to/my_configs # or --config-dir on every command
155
+ hbtsim catalog validate # schema + build check, names the field on error
156
+ hbtsim snr --target my_target --instrument keck_pair
157
+ ```
158
+
159
+ Files on the search path override shipped ones of the same name;
160
+ `extends` derives a definition from another (`spad_lambda_ng` is
161
+ `spad_lambda` with a correlator readout). `hbtsim catalog show target
162
+ deltavel` prints a definition with its provenance; `hbtsim catalog list`
163
+ the names; `from hbtsim import load_target, load_array` the Python side.
164
+
165
+ ## Physics
166
+
167
+ For a chaotic (thermal) source, the Siegert relation links the measured
168
+ intensity correlation to the first-order coherence:
169
+
170
+ g²(B) = 1 + |V(B)|²,
171
+
172
+ where V(B) is the complex degree of coherence, the normalized Fourier
173
+ transform of the sky brightness distribution at spatial frequency
174
+ **u** = **B**/λ (van Cittert–Zernike). Numerically:
175
+
176
+ image I(θx, θy) → exact K-point DFT at (u, v) = B/λ (separable
177
+ matrix products, float32-exact phase)
178
+ → V(u, v) / V(0, 0) → g²(B) = 1 + |V(B)|².
179
+
180
+ Each star is a limb-darkened disk whose centre-to-limb profile is
181
+ tabulated on a μ grid: by default the linear law I(μ)/I(1) = 1 − u_λ(1 − μ)
182
+ with per-star Claret & Bloemen (2011) coefficients, weighted by the
183
+ Planck function at its effective temperature; with `--newera-dir`
184
+ (`hbtsim.sed.NewEraGrid`, `with_newera`) the angle-resolved intensities
185
+ I(μ, λ) and surface fluxes of the NewEra PHOENIX models (Hauschildt et
186
+ al. 2025; `scripts/prepare_newera.py` bins an HSR-RF file to a 0.02 nm
187
+ table; `hbtsim data fetch` pulls the binned tables from NERSC, whose grid
188
+ covers 8000–12 000 K × log g 3.0–4.5), interpolated bilinearly in T_eff and
189
+ log g per star, which then set both the flux ratio of the two stars
190
+ (hence the fringe contrast) and their limb profiles, line by line.
191
+ Three things a spherical model needs are built in: its μ = 0 is the
192
+ model's outer boundary, drawn at R_outer = (1.004–1.008) × the τ = 1
193
+ radius the catalogue quotes (`Star.radius_ref`,
194
+ `BinarySystem.drawn_radius_mas`); each star renders on its own μ nodes
195
+ so the drawn profile is exactly the one `disk_flux_factor` integrates;
196
+ and the 0.02 nm tables are averaged over each spectrograph channel
197
+ (`sed.prepare_system`), optionally Doppler-shifted by the orbital
198
+ radial velocities (`BinarySystem.doppler`) and rotationally broadened
199
+ (`Star.vsini_kms`); interstellar reddening (`BinarySystem.a_v`, CCM89)
200
+ and an air-wavelength channel grid (`Spectrograph.frame`) are hooks
201
+ with defaults off. Eclipses are handled by z-ordering the
202
+ disks on the grid, which also yields the lightcurves by direct image
203
+ summation. Lightcurves are calibrated in two steps: synthetic
204
+ monochromatic AB magnitudes from the physical flux at Earth, then a
205
+ constant per-band shift anchoring maximum light to the observed
206
+ photometry (for blackbody photospheres the zero point is a few tenths
207
+ of a magnitude off — no line blanketing or H⁻ opacity; with model SEDs
208
+ the anchors only serve as a check, and eclipse shapes and depths are
209
+ purely simulated in both cases).
210
+
211
+ The g² = 1 + |V|² normalization is the ideal fully-coherent-detection
212
+ limit; a real intensity interferometer measures a contrast reduced by
213
+ the ratio of coherence time to detector resolution (Rai, Basak & Saha
214
+ 2021, eq. 6) — that physics lives in the SNR module below.
215
+
216
+ Numerical layout: 1024² source grid at 0.01 mas/pixel (disk radii
217
+ ~45–55 px, limb darkening well resolved); `GridConfig.fit_orbit()` (the
218
+ renderer default) grows the grid to hold an orbit (δ Vel's 16.6 mas
219
+ needs 2048 px), `GridConfig.for_system()` also refines the scale for
220
+ stars below 50 px, and
221
+ `GridConfig(supersample=4)` renders each pixel as the mean of 16
222
+ sub-pixel soft-rim renders (with the exact Dirichlet pixel window of
223
+ the s × s average removed from the DFT), which takes the renderer's
224
+ limb bias from ~1e-4 to ~1e-5 in
225
+ |V|² and the rendered-vs-analytic closure phase from 0.15° to 0.02°.
226
+ The visibility is *not*
227
+ taken from a padded FFT map: the interferometer only ever needs V at a
228
+ few points per channel, and the K-point DFT (`hbtsim/hbt.py`) gives them
229
+ exactly — no interpolation error, no crop limit on the baseline, ~1000×
230
+ fewer operations than the 8192² FFT it replaced (which survives in
231
+ `hbtsim/fftmap.py` for 2-D maps and cross-checks). The pipeline is
232
+ validated against analytic results (Airy nulls, limb-darkened Bessel
233
+ series, the binary fringe formula, circle-overlap eclipse depths) in the
234
+ test suite; the DFT core agrees with a float64 reference to 1e-6 in
235
+ |V|² and 1e-5 rad in phase.
236
+
237
+ ## Observation SNR (hbtsim.snr)
238
+
239
+ `hbtsim/snr.py` estimates the signal-to-noise of a g² measurement with a
240
+ pair of telescopes and photon-counting detectors: detected stellar rates
241
+ from the anchored source model, excess coincidences
242
+ N_sig = ½|V|²·τ_c·R₁R₂·T for unpolarized light spread over the
243
+ detectors' combined timing jitter, matched-filtered against the
244
+ accidental floor (dark and sky counts included),
245
+
246
+ SNR = ½ |V|² τ_c R₁ R₂ √T / √(b₁ b₂ · 2√π σ_pair),
247
+
248
+ with per-pixel non-paralyzable dead time. Everything is parameterized
249
+ via `Telescope`, `Detector`, `Observation` and `Spectrograph` dataclasses,
250
+ built from the catalog (`load_telescope("keck_10m")`). The instrument
251
+ model carries the effects a real implementation cannot escape:
252
+
253
+ - **Throughput** is telescope (atmosphere + optics, 0.3) × backend
254
+ (0.9 for a narrow-band filter, **0.5 for a cross-dispersed
255
+ spectrograph** — every dispersed channel sees 0.15, not 0.3) × PDE.
256
+ - **Constant resolving power**: `Spectrograph.from_resolving_power(5000)`
257
+ builds the geometric channel grid an R ≈ 5000 spectrograph actually
258
+ has (4325 channels of 0.08–0.19 nm over 400–950 nm), not 5500
259
+ uniform 0.1 nm channels.
260
+ - **Readout**: a bright star dispersed over thousands of channels
261
+ delivers 10¹⁰–10¹¹ detected photons/s per 8–10 m telescope, far
262
+ beyond any time-tag link. `Detector.readout` is `"timetag"` (the
263
+ SPAD Lambda as delivered, USB3, `max_total_cps` = 1.4 × 10⁸, the
264
+ manufacturer's 140 Mcps — the spectral functions scale the rates
265
+ down to the ceiling and flag `readout_limited`) or `"correlator"`
266
+ (the next-generation design, `spad_lambda_ng`: a real-time correlator
267
+ with no link limit, either both beams on one sensor or tags streamed
268
+ to a central FPGA/GPU; an idealization, not an existing system). A
269
+ per-pixel dead-time load r·τ_dead > 1 raises a warning (the
270
+ non-paralyzable model is unreliable there; spread the light over
271
+ more pixels).
272
+ - **Polarization**: `polarization_mode="pbs"` (a polarizing beamsplitter
273
+ into two detectors per telescope) gains √2 in g² SNR and ×2 in g³ at
274
+ the same photon budget while halving the per-pixel load;
275
+ `"single_pol"` gains nothing; the default is unpolarized (p₂ = ½,
276
+ p₃ = ¼).
277
+ - **Coherence broadening** of the correlation kernel (σ_c = 0.376 τ_c)
278
+ — a ~1 % loss at 0.1 nm in the red, on by default.
279
+
280
+ <!-- catalog:hardware -->
281
+ Telescopes, detectors, spectrographs, sites and arrays are JSON files in
282
+ `hbtsim/configs/` (`hbtsim catalog list`); the shipped ones:
283
+
284
+ - **Telescopes**: `c2pu_1m` (1 m, throughput 0.3); `eonsii_4m` (4 m, 9 m², throughput 0.64); `gtc_10p4m` (10.4 m, 78.54 m², throughput 0.3); `int_2p54m` (2.54 m, throughput 0.3); `keck_10m` (10 m, throughput 0.3); `kk_4m` (4 m, throughput 0.3); `lst1_23m` (23 m, 390 m², throughput 0.304); `magic_17m` (17 m, 236 m², throughput 0.304); `not_2p56m` (2.56 m, throughput 0.3); `subaru_8p2m` (8.2 m, throughput 0.3); `tng_3p58m` (3.58 m, throughput 0.3); `veritas_12m` (12 m, 110 m², throughput 0.3); `vlt_ut_8p2m` (8.2 m, throughput 0.3); `wht_4p2m` (4.2 m, 13.8 m², throughput 0.3).
285
+ - **Detectors**: `eonsii_mcp_pmt` (46 ps FWHM, 1 ns dead, 1 cps dark, timetag, link ≤ 1e+09 cps); `eonsii_spad` (20 ps FWHM, 10 ns dead, 250 cps dark, timetag, link ≤ 1e+09 cps); `eonsii_spad_correlator` (20 ps FWHM, 10 ns dead, 250 cps dark, correlator); `kk_ideal` (39 ps FWHM, 0 ns dead, 0 cps dark, correlator); `lpqi_spad64_i2cass` (500 ps FWHM, 10 ns dead, 1.68 cps dark, timetag, link ≤ 6.7e+06 cps); `lpqi_spad_nextgen` (100 ps FWHM, 10 ns dead, 1.68 cps dark, timetag, link ≤ 4e+07 cps); `magic_pmt` (8540 ps FWHM, 0 ns dead, 0 cps dark, correlator); `spad_lambda` (120 ps FWHM, 10 ns dead, 250 cps dark, timetag, link ≤ 1.4e+08 cps); `spad_lambda_ng` (120 ps FWHM, 10 ns dead, 250 cps dark, correlator); `veritas_pmt` (7515 ps FWHM, 0 ns dead, 0 cps dark, correlator).
286
+ - **Spectrographs**: `eonsii_1000ch` (400–550 nm, 1000 ch, 0.15 nm channels, throughput 0.6); `eonsii_60ch` (400–550 nm, 60 ch, 2.50 nm channels, throughput 0.6); `eonsii_r7500` (400–550 nm, 2389 ch, R = 7500, throughput 0.6); `filter_2mass_h` (1480–1780 nm, 1 ch, 300.00 nm channels, throughput 1); `filter_2mass_k` (1995–2385 nm, 1 ch, 390.00 nm channels, throughput 1); `filter_cousins_i` (732–880 nm, 1 ch, 149.00 nm channels, throughput 1); `filter_cousins_r` (589–727 nm, 1 ch, 138.00 nm channels, throughput 1); `filter_johnson_b` (398–492 nm, 1 ch, 94.00 nm channels, throughput 1); `filter_johnson_v` (507–595 nm, 1 ch, 88.00 nm channels, throughput 1); `filter_lpqi_500_1nm` (500–500 nm, 1 ch, 1.00 nm channels, throughput 0.9); `filter_lpqi_550_1nm` (550–550 nm, 1 ch, 1.00 nm channels, throughput 0.9); `filter_lpqi_halpha_1nm` (656–657 nm, 1 ch, 1.00 nm channels, throughput 0.9); `filter_lpqi_hbeta_1nm` (486–487 nm, 1 ch, 1.00 nm channels, throughput 0.9); `hbeta_window_r5000` (471–502 nm, 320 ch, R = 5000, throughput 0.5); `kk_1000ch_400_950` (400–950 nm, 1000 ch, 0.55 nm channels, throughput 1); `r5000_400_950` (400–950 nm, 4325 ch, R = 5000, throughput 0.5); `spad_lambda_320` (400–950 nm, 320 ch, 1.72 nm channels, throughput 0.5).
287
+ - **Arrays**: `c2pu_pair` (2 stations, 15 m at Calern (C2PU)) (generator: `load_array(name, side_m=…)`); `eonsii_pair_teide` (2 stations, 100 m at Teide (Izana)) (generator: `load_array(name, side_m=…)`); `eonsii_triangle_paranal` (3 stations, 20–20 m at Paranal) (generator: `load_array(name, side_m=…)`); `keck_pair` (2 stations, 85 m at Maunakea); `lpqi_orm` (5 stations, 447–1547 m at Roque de los Muchachos, Nordic Optical Telescope (LPQI reference point)); `lpqi_pathfinder` (2 stations, 550 m at Roque de los Muchachos, Nordic Optical Telescope (LPQI reference point)) (generator: `load_array(name, side_m=…)`); `magic_lst1` (3 stations, 86–100 m at Roque de los Muchachos (MAGIC, LST-1)); `maunakea_subaru_keck` (3 stations, 85–226 m at Maunakea); `veritas` (4 stations, 82–173 m at Fred Lawrence Whipple Observatory (VERITAS)); `vlt_ut` (4 stations, 47–130 m at Paranal).
288
+ - **Sites**: `calern` (Calern (C2PU)); `flwo` (Fred Lawrence Whipple Observatory (VERITAS)); `maunakea` (Maunakea); `orm` (Roque de los Muchachos (MAGIC, LST-1)); `orm_not` (Roque de los Muchachos, Nordic Optical Telescope (LPQI reference point)); `paranal` (Paranal); `teide` (Teide (Izana)).
289
+ <!-- /catalog -->
290
+
291
+ Two observing modes (`hbtsim snr --target spica --instrument keck_pair`, or
292
+ `--telescope keck_10m --detector spad_lambda_ng --baseline 85`):
293
+
294
+ - **Spectral (default)** — the light is dispersed along the SPAD
295
+ Lambda's 320-pixel array: each pixel pair is an independent ~1.7 nm
296
+ channel measuring its own g², and channel SNRs add in quadrature
297
+ (~√320 ≈ 18× multiplexing gain). Per-channel |V|²(B, λ) comes from the
298
+ batched render + DFT pipeline (valid through eclipses), averaged over
299
+ the two apertures; `--vis2-method analytic` is the instant
300
+ out-of-eclipse alternative (agrees to 1e-3). `--resolving-power R`
301
+ switches to a constant-R grid, `--readout correlator` lifts the
302
+ time-tag ceiling, `--polarization pbs` adds the beamsplitter,
303
+ `--n-pixels` spreads each channel over several pixels.
304
+ - **Narrowband** — single filters (`--mode narrowband --wavelengths 400
305
+ 800 --filter-width 10`).
306
+
307
+ Headline numbers for Beta Aurigae at quadrature, 1 h
308
+ (`hbtsim run g2_c2pu`, docs §5f): a single 10 nm filter on
309
+ C2PU gives SNR ≈ 0.1–0.9; the 320-channel SPAD Lambda with a correlator
310
+ readout lifts that to **≈ 15/√h at B = 35 m** (8 at 50 m) — the
311
+ time-tag link caps it at ≈ 7 — and an R = 5000 backend to 60 (54 with
312
+ the NewEra tables); the Keck pair at 85 m reaches 240/√h with 320
313
+ channels (σ_|V|² ≈ 0.02 per channel-hour) and the EON-SII 4 m pair at
314
+ 30 m ≈ 570/√h with 1000 channels, both link-limited. The EON-SII
315
+ design study quotes 10–25× shorter times; `hbtsim run mc_sirius_b_eonsii`
316
+ reruns its Sirius B Monte Carlo and shows its hours need a coincidence
317
+ window of 2–6 ps, well below the detectors' 12–28 ps jitter (docs §5e.1).
318
+ The matched filter stays the default. On single stars
319
+ (`hbtsim run chromatic_sirius_vega_eonsii`, `docs/chromatic_diameters_eonsii.md`)
320
+ EON-SII measures Sirius's and Vega's Balmer-core diameters (+3.6 to
321
+ +4.7 % over the continuum at Hβ–Hδ) at 5σ in 1–2 nights through its
322
+ links, or in minutes with channel-subset tagging. Keck caveats: a
323
+ single SPAD pixel saturates at Keck count rates (`--n-pixels`), and a
324
+ 10 m aperture on an 85 m baseline averages |V|² over B ± 10 m
325
+ (modeled: `hbtsim.aperture`).
326
+
327
+ The **LPQI-Pathfinder** (La Palma Quantum Interferometer: NOT 2.56 m +
328
+ TNG 3.58 m at 550 m, 64×64 SPAD cameras behind 1 nm filters, one filter per
329
+ night; later GTC, WHT and INT to 1.5 km) is in the catalog as
330
+ `arrays/lpqi_pathfinder`, `arrays/lpqi_orm`, the `lpqi_*` detectors,
331
+ filters and backends, with every unpublished number flagged;
332
+ [docs/lpqi_pathfinder.md](docs/lpqi_pathfinder.md) shows that at 550 m
333
+ all of the catalog's stars are resolved out and what the 1 nm / 500 ps
334
+ budget means for them (`hbtsim run g2_binaries_lpqi_pathfinder`).
335
+
336
+ ## g²(λ) movies with error bars
337
+
338
+ `hbtsim g2spec --target algol --instrument keck_pair`
339
+ computes one frame per hour over a full orbit (69 epochs for Algol, 96
340
+ for Beta Aur): per-channel simulated measurements with 1σ error bars
341
+ (inverse-variance 8-channel bins highlighted) over the true model curve,
342
+ beside the temperature-colored sky view. During Algol's primary eclipse
343
+ the blue channels dim by 1.5 mag and the error bars visibly inflate
344
+ while the fringe spectrum collapses. Compute (`--compute-only`, GPU) and
345
+ rendering (`--render-only`, needs ffmpeg) are separable via the npz file.
346
+
347
+ ## Three telescopes: bispectrum and closure phase
348
+
349
+ Two-point HBT gives only |V|² — no Fourier phase. With three telescopes
350
+ the triple correlation g³ = 1 + Σ|γᵢⱼ|² + 2|γ₁₂γ₂₃γ₃₁|cos φc carries the
351
+ **closure phase** (the bispectrum phase, immune to per-telescope phase
352
+ and source translation), the entry point to image reconstruction.
353
+ `hbtsim/bispectrum.py` samples complex visibilities from the rendered
354
+ image by the same exact DFT (validated against the analytic binary to
355
+ <0.1° in closure phase, renderer-limited), defines
356
+ telescope triangles (catalog arrays: `maunakea_subaru_keck` — Subaru +
357
+ Keck I + Keck II at 152/85/226 m, from site coordinates; `vlt_ut`), projects
358
+ them onto the (u, v) plane for a real hour angle and declination
359
+ (`Triangle.projected`, `hbtsim.geometry`), and computes closure phases
360
+ through eclipses via a GPU-batched spectral path.
361
+
362
+ Two effects a real correlator cannot avoid are modeled by default in
363
+ the SNR functions: **aperture smearing** — a 10 m pupil on an 85 m
364
+ baseline averages |V|² over B ± 10 m, which keeps only
365
+ A(πD₁ρ/λ)·A(πD₂ρ/λ) of a binary fringe's contrast (0.66 for Keck on
366
+ β Aur at 400 nm, 0.45 for the VLT UTs on δ Vel at maximum
367
+ separation); the triple product is averaged exactly over all three
368
+ pupils (`hbtsim.aperture`) — and **sky orientation**: every system
369
+ carries the position angle of its ascending node, so closure phases
370
+ on a fixed ground triangle are computed at the true orientation, and
371
+ `snr3.track_g3_snr` integrates a night block by block along the uv
372
+ track, flagging blocks over which the fringe drifts by more than 1/8
373
+ cycle.
374
+ `hbtsim/snr3.py` extends the photon budget to triple coincidences
375
+ (pol₃ = ¼, 2D lag-plane matched filter; SNR₃ ∝ 1/σ_jitter and
376
+ ∝ Δλ^(−1/2) — narrowband multiplexing is the lever).
377
+
378
+ **Feasibility verdict** ([docs/three_telescope_feasibility.md](docs/three_telescope_feasibility.md),
379
+ `hbtsim run g3_{spica,deltavel}_vlt`, `g3_{betaaur,algol}_maunakea`): the Subaru arms resolve out the ~1 mas
380
+ disks (triple amplitude ≲ 0.04) and with the stock 320-channel SPAD
381
+ Lambda the closure phase of Algol needs **centuries**; an R ≈ 5000
382
+ backend with a correlator readout brings the template detection of
383
+ Algol's closure phase (Δcos φc ≤ 0.1) to **~200 nights** along the uv
384
+ track. Geometry beats aperture — and target selection rescues the real
385
+ triangle: **Spica** (`--system spica`; V = 0.97, hot small-disk B-star
386
+ pair whose primary diameter was itself measured by the Narrabri
387
+ intensity interferometer) reaches Δcos φc ≤ 0.1 in **~11 nights** on
388
+ Subaru + Keck I + Keck II with the R ≈ 5000 backend (2 with a
389
+ polarizing beamsplitter).
390
+
391
+ The best configuration studied is the **VLT 4×UT array**
392
+ (`bispectrum.VLT_UT`: 8.2 m × 4, baselines 46.6–130.2 m — all inside
393
+ Spica's first null, four simultaneous triangles + six |V|² baselines):
394
+ combined bispectrum sensitivity ≈ 13/√h (29 with a beamsplitter) on
395
+ Spica with the R ≈ 5000 backend → **Δcos φc ≤ 0.1 in ~36 minutes**
396
+ (7 with the beamsplitter), R = 100 closure-phase *curves* in 8.6 nights
397
+ (14 h) per epoch around the 4-day orbit, and a one-night limiting
398
+ magnitude of g ≈ 2.2 (southern targets only; Paranal cannot see
399
+ Algol/β Aur). δ Vel, at its correct distance of 25.1 pc, has a 4.7 m
400
+ blue fringe period at maximum separation that 8 m pupils smear out (it
401
+ returns near conjunction for ~21 % of the orbit). It is a 1–4 m-class g²
402
+ target (C2PU, EON-SII). Three EON-SII units on a 12 m triangle at
403
+ Paranal (`arrays/eonsii_triangle_paranal`, `snr3.campaign_g3_snr`,
404
+ `hbtsim run g3_deltavel_eonsii_paranal`) detect its closure-phase template in ~20
405
+ nights over an orbit. The asymmetric part of its closure phase
406
+ (φc ≠ 0, π) needs ~10⁴ nights on any array considered, because g³ sees
407
+ only cos φc (docs §5g).
408
+
409
+ ## Batched spectral pipeline (CPU/GPU)
410
+
411
+ `hbtsim.spectral.spectral_vis(pos, bvecs, wavelengths, system, grid)`
412
+ (and the `spectral_vis2` wrapper for scalar baselines along a position
413
+ angle) renders the binary and evaluates its DFT at the baseline vectors
414
+ for every channel inside one jitted JAX computation; channels run in
415
+ chunks via `jax.lax.map(batch_size=...)` (renders and matrix products
416
+ batched per chunk, ~30 MB of temporaries per channel; default chunk 64
417
+ on GPU, 8 on CPU). Measured on an Intel iMac Pro (20 cores, CPU JAX):
418
+ **1.2 ms/channel** (320 channels in 0.37 s steady state), ~380× faster
419
+ than the padded-FFT version of this pipeline (460 ms/channel). On a
420
+ *shared* GPU set `XLA_PYTHON_CLIENT_PREALLOCATE=false` (XLA's 75%
421
+ preallocation fights other users).
422
+
423
+ ```bash
424
+ python scripts/bench_spectral.py # benchmark, default device
425
+ JAX_PLATFORMS=cpu python scripts/bench_spectral.py # force CPU
426
+ ```
427
+
428
+ ## Campaigns (`hbtsim run`)
429
+
430
+ Every study behind the docs and the paper is a campaign file in
431
+ `hbtsim/configs/campaigns/`: the target(s), array or telescope, backends,
432
+ observing window and runner-specific options, resolved through the same
433
+ catalog as everything else. `hbtsim run <name>` (or a path to your own
434
+ `.json`) writes `output/campaigns/<name>/{results.json, table.md,
435
+ table.tex, log.txt, figures}`; `--set key=value` edits any field for one
436
+ run (`--set night.block_minutes=30`, `--set atmosphere.use=false`),
437
+ `--no-figures`, `--no-track`, `--newera-dir` / `--no-newera` as on the
438
+ other tools. `results.json` records the resolved campaign definition, its
439
+ content hash and the git revision, so a number in a table can always be
440
+ traced to its inputs. `suite_phase6` runs everything (`--jobs 4`).
441
+
442
+ <!-- catalog:campaigns -->
443
+ | Campaign | Runner | What it computes |
444
+ |---|---|---|
445
+ | `chromatic_c2pu_regression` | chromatic | Chromatic-diameter regression: Sirius A and Vega, 2 x 1 m, 320 ch at R = 5000 around H-beta, 6 h |
446
+ | `chromatic_sirius_vega_eonsii` | chromatic | Chromatic (wavelength-dependent) diameters of Sirius A and Vega with the EON-SII pair from Teide |
447
+ | `g2_binaries_lpqi_pathfinder` | g2 | The four binaries on the LPQI-Pathfinder (NOT + TNG, 550 m, 1 nm filters, one filter per night) |
448
+ | `g2_c2pu` | g2 | Two-telescope g2 sensitivity of the four binaries on the C2PU 1 m pair |
449
+ | `g2_eonsii` | g2 | Two-telescope g2 sensitivity of the four binaries on the EON-SII pair |
450
+ | `g2_keck` | g2 | Two-telescope g2 sensitivity of the four binaries on the Keck pair |
451
+ | `g3_algol_maunakea` | g3 | Algol closure phases with Subaru + Keck I + Keck II |
452
+ | `g3_betaaur_maunakea` | g3 | beta Aur closure phases with Subaru + Keck I + Keck II |
453
+ | `g3_deltavel_eonsii_paranal` | g3_campaign | delta Vel closure-phase campaign with three EON-SII units at Paranal |
454
+ | `g3_deltavel_vlt` | g3 | delta Vel closure phases with the four VLT UTs |
455
+ | `g3_spica_vlt` | g3 | Spica closure phases with the four VLT UTs (docs/three_telescope_feasibility.md) |
456
+ | `mc_sirius_b_eonsii` | montecarlo | Sirius B diameter Monte Carlo with the EON-SII pair (paper Table 2 cross-check) |
457
+ | `mc_sirius_b_lpqi_pathfinder` | montecarlo | Sirius B diameter Monte Carlo on the LPQI-Pathfinder (NOT + TNG, 550 m, one 1 nm filter) |
458
+ | `redclump_ii_dwarf` | scale | Red-clump scale precision (Kim & Kaiser comparison) with the 4800 K / log g 4.5 dwarf stand-in |
459
+ | `redclump_ii_supergiant` | scale | Red-clump scale precision (Kim & Kaiser comparison) with the 5000 K / log g 0 supergiant stand-in |
460
+ | `suite_phase6` | suite | Everything behind docs/three_telescope_feasibility.md and the paper tables (output/logs/run_*.sh) |
461
+ <!-- /catalog -->
462
+
463
+ ## Installing
464
+
465
+ ```bash
466
+ pip install hbtsim # CPU JAX; add "hbtsim[movie]" for the movies (matplotlib + ffmpeg)
467
+ hbtsim catalog validate # the shipped JSON catalog
468
+ hbtsim snr --target spica --instrument keck_pair --vis2-method analytic
469
+ ```
470
+
471
+ Python 3.11+; `pip install "hbtsim[gpu]"` for CUDA JAX, `"hbtsim[sed]"`
472
+ for h5py (only `scripts/prepare_newera.py` needs it). From a checkout:
473
+ `uv venv .venv && uv pip install -p .venv/bin/python -e ".[test,movie]"`.
474
+
475
+ ### Model atmospheres (NewEra tables)
476
+
477
+ The NewEra PHOENIX tables that replace the blackbody + Claret model are not
478
+ in the package. They are hosted at NERSC
479
+ (`https://portal.nersc.gov/project/newera/binned/`, the `remote` block of
480
+ `hbtsim/configs/resources/*.json`) and cached on your machine:
481
+
482
+ ```bash
483
+ hbtsim data path # where tables are looked for
484
+ hbtsim data fetch --target betaaur # the four grid corners beta Aur needs (~48 MB)
485
+ hbtsim data fetch --target algol --allow-extrapolation
486
+ hbtsim data fetch --all # every 380-1000 nm table (19 files, 210 MB)
487
+ hbtsim data fetch --resource newera_redclump --model lte04800-4.50-0.0
488
+ hbtsim data list # what the cache holds
489
+ ```
490
+
491
+ Every tool takes `--fetch` to pull what its target needs first
492
+ (`HBTSIM_AUTO_FETCH=1` does it always); without tables the stars fall back
493
+ to blackbody + linear limb darkening and the report says so. Environment
494
+ variables: `HBTSIM_DATA_DIR` (cache root; default `~/Library/Caches/hbtsim`
495
+ on macOS, `~/.cache/hbtsim` on Linux), `HBTSIM_DATA_URL` (mirror base URL,
496
+ `file://` works), `HBTSIM_NEWERA_DIR` / `HBTSIM_NEWERA_REDCLUMP_DIR` (an
497
+ explicit directory per resource), `HBTSIM_CONFIG_PATH` (your own catalog
498
+ files). A checkout's `data/newera/` is used when present. Hosting a mirror
499
+ is one command: `hbtsim data manifest <dir> --write` next to the tables.
500
+
501
+ ## Running from a checkout
502
+
503
+ ```bash
504
+ # one-time setup (macOS)
505
+ brew install ffmpeg
506
+ uv venv --python 3.13 .venv
507
+ uv pip install -p .venv/bin/python -e ".[test,movie]" # + [sed] for h5py, [gpu] for CUDA
508
+
509
+ # what is in the catalog
510
+ .venv/bin/python -m hbtsim catalog list
511
+ .venv/bin/python -m hbtsim catalog show target spica
512
+
513
+ # orbit movies (240 frames, ~1 min each on CPU); also installed as hbtsim-movie
514
+ .venv/bin/python -m hbtsim movie --target betaaur
515
+ .venv/bin/python -m hbtsim movie --target algol
516
+
517
+ # SNR tables (hbtsim-snr)
518
+ .venv/bin/python -m hbtsim snr --target algol --instrument keck_pair --baseline 50 85
519
+
520
+ # tests (analytic validation suite, ~65 s)
521
+ .venv/bin/python -m pytest
522
+
523
+ # feasibility campaigns with the NewEra tables (hbtsim data fetch --all first, or --fetch)
524
+ .venv/bin/python -m hbtsim run g3_betaaur_maunakea
525
+ .venv/bin/python -m hbtsim run g2_eonsii
526
+ .venv/bin/python -m hbtsim run suite_phase6 --jobs 4 # everything behind the docs
527
+ ```
528
+
529
+ ## Running on NERSC Perlmutter
530
+
531
+ ```bash
532
+ git clone https://github.com/nugent68/hbtsim.git ~/hbtsim
533
+ bash ~/hbtsim/scripts/perlmutter/setup_env.sh # conda env jax-gpu-env
534
+ ```
535
+
536
+ The env uses pip `jax[cuda12]` wheels, which bundle CUDA/cuDNN — do
537
+ **not** `module load cudatoolkit cudnn nccl` (their LD_LIBRARY_PATH
538
+ entries shadow the bundled libraries and break JAX).
539
+
540
+ Login nodes carry a *shared* A100: fine for short runs with
541
+ `XLA_PYTHON_CLIENT_PREALLOCATE=false` and `--chunk 4`, but contention
542
+ can OOM you. For real work use a dedicated GPU:
543
+
544
+ ```bash
545
+ # interactive (account: your _g allocation)
546
+ salloc -N 1 -C gpu -G 1 -c 32 -q interactive -t 30 -A m2218_g
547
+ module load python && conda activate jax-gpu-env
548
+ cd ~/hbtsim && srun -n 1 python -m hbtsim g2spec --target algol \
549
+ --compute-only --diameter 10 --baseline 85 \
550
+ --npz $SCRATCH/hbt/g2spec_algol_keck.npz
551
+ # (note: standalone srun needs an explicit -n 1)
552
+
553
+ # or batch
554
+ sbatch ~/hbtsim/scripts/perlmutter/bench.sbatch
555
+ ```
556
+
557
+ Keep the repo and env in `$HOME` ($SCRATCH is purged after ~8 weeks);
558
+ write job outputs to `$SCRATCH/hbt/` (keep them out of the scratch root —
559
+ `mkdir -p $SCRATCH/hbt` once), then copy the npz back and render the
560
+ movie locally with `--render-only`.
561
+
562
+ ## Package layout
563
+
564
+ - `hbtsim/configs/` — the catalog: one JSON file per band table,
565
+ limb-darkening table, target, telescope, detector, spectrograph,
566
+ backend, site, array, data resource and campaign (single source of
567
+ truth; `sources` / `notes` / `provenance` / `assumptions` carry the
568
+ literature trail)
569
+ - `hbtsim/catalog/` — loads and validates the catalog (`Catalog`,
570
+ `load_target`, `load_array`, …; `extends`, search path, `hbtsim catalog`
571
+ CLI); `hbtsim/serialize.py` — canonical JSON / content hashes of the
572
+ built objects (the campaign cache key)
573
+ - `hbtsim/params.py` — constants, `Star`/`BinarySystem`/`DiskTarget`/
574
+ `GridConfig`/`MovieConfig`
575
+ - `hbtsim/cli_common.py`, `hbtsim/__main__.py` — the shared `--target` /
576
+ `--instrument` / `--config-dir` / `--fetch` options and the `hbtsim
577
+ <command>` dispatcher
578
+ - `hbtsim/data.py` — the NewEra table cache and fetcher (`hbtsim data
579
+ fetch|list|path|manifest`; NERSC portal manifest, sha256-verified)
580
+ - `hbtsim/orbit.py` — Keplerian sky geometry, oriented on the sky by Ω
581
+ - `hbtsim/aperture.py` — finite-aperture (pupil) averaging of |V|² and
582
+ of the three-pupil bispectrum
583
+ - `hbtsim/geometry.py` — `Site`, hour angle, uv projection, fringe drift
584
+ - `hbtsim/limbdark.py` — linear LD law, analytic and numeric (tabulated
585
+ profile) disk visibilities
586
+ - `hbtsim/sed.py`, `scripts/prepare_newera.py` — model-atmosphere flux
587
+ and I(μ, λ) tables (NewEra PHOENIX HSR-RF reader), the `NewEraGrid`
588
+ (T_eff, log g) interpolation, channel averaging, Doppler / rotational
589
+ broadening / extinction hooks
590
+ - `hbtsim/render.py` — JAX rendering of the occulted limb-darkened disks
591
+ - `hbtsim/hbt.py` — exact K-point DFT sampling of V(u, v), |V|², g²(B);
592
+ analytic binary visibility
593
+ - `hbtsim/fftmap.py` — padded-FFT 2-D maps (plots/cross-checks only)
594
+ - `hbtsim/spectral.py` — batched multi-wavelength V(B, λ) (GPU)
595
+ - `hbtsim/photometry.py` — band fluxes, AB magnitudes, anchoring
596
+ - `hbtsim/snr.py`, `hbtsim/snr_cli.py` — photon-budget SNR (telescopes,
597
+ detectors, spectrograph multiplexing)
598
+ - `hbtsim/snr3.py` — triple-correlation (closure-phase) SNR, uv tracks,
599
+ shared-geometry backends and multi-night campaigns
600
+ (`campaign_g3_snr`)
601
+ - `hbtsim/estimators.py`, `hbtsim/montecarlo.py` — the EON-SII design
602
+ study's photon-level and classic-HBT estimators beside the matched
603
+ filter, and a Poisson coincidence-histogram Monte Carlo of a
604
+ multiplexed diameter measurement
605
+ - `hbtsim/single.py`, `hbtsim/chromatic.py` — single stars (Sirius A,
606
+ Vega) with NewEra profiles, smeared uniform-disk inversion, and
607
+ Balmer-core vs continuum (chromatic) diameters
608
+ - `hbtsim/movie.py`, `hbtsim/cli.py` — the 3-panel orbit movie
609
+ - `hbtsim/g2spec.py` — the g²(λ)-with-error-bars movie
610
+ - `scripts/` — `catalog_tables.py` (renders the parameter tables of this
611
+ README and the docs from the catalog), `feasibility_g3.py` (every number in the docs),
612
+ `sed_compare.py` (blackbody vs NewEra), `eonsii_crosscheck.py` and
613
+ `eonsii_montecarlo.py` (the EON-SII sensitivity gap),
614
+ `deltavel_eonsii.py` (δ Vel closure phases with three EON-SII units),
615
+ `chromatic_diameters.py` (Sirius/Vega Balmer-core diameters),
616
+ benchmark + Perlmutter setup/sbatch
617
+ - `tests/` — analytic validation tests (`pytest tests/`, ~330 tests,
618
+ ~75 s; the NewEra-data tests skip without `data/newera/`); the objects
619
+ come from the catalog through the fixtures of `tests/conftest.py`
620
+
621
+ ## References
622
+
623
+ - Hanbury Brown, R. & Twiss, R. Q. 1956, Nature 178, 1046
624
+ - Rai, K. N., Basak, S. & Saha, P. 2021, MNRAS (arXiv:2105.09532) —
625
+ binary-star intensity interferometry formalism
626
+ - Guerin, W. et al. 2025, Comptes Rendus Physique (arXiv:2503.22446) —
627
+ SII in the photon-counting regime
628
+ - Southworth, J., Bruntt, H. & Buzasi, D. L. 2007, A&A 467, 1215 —
629
+ Beta Aurigae absolute dimensions
630
+ - Hummel, C. A. et al. 1995, AJ 110, 376 — Beta Aur interferometric orbit
631
+ - Baron, F. et al. 2012, ApJ 752, 20 — Algol CHARA/MIRC imaging
632
+ - Zavala, R. T. et al. 2010, ApJ 715, L44 — Algol orbit and parallax
633
+ - Kolbas, V. et al. 2015, MNRAS 451, 4150 — Algol spectral disentangling
634
+ - Claret, A. & Bloemen, S. 2011, A&A 529, A75 — limb-darkening coefficients
635
+ - Jester, S. et al. 2005, AJ 130, 873 — Johnson→SDSS transformations