PyTransit 2.9.0__tar.gz → 2.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 (165) hide show
  1. {pytransit-2.9.0 → pytransit-2.9.1}/PKG-INFO +1 -1
  2. {pytransit-2.9.0 → pytransit-2.9.1}/PyTransit.egg-info/PKG-INFO +1 -1
  3. {pytransit-2.9.0 → pytransit-2.9.1}/PyTransit.egg-info/SOURCES.txt +5 -1
  4. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/__init__.py +1 -1
  5. pytransit-2.9.1/pytransit/utils/io/__init__.py +20 -0
  6. pytransit-2.9.1/pytransit/utils/io/base.py +385 -0
  7. pytransit-2.9.1/pytransit/utils/io/lcdata.py +322 -0
  8. pytransit-2.9.1/pytransit/utils/io/rvdata.py +192 -0
  9. pytransit-2.9.1/tests/test_lcdata.py +526 -0
  10. pytransit-2.9.1/tests/test_rvdata.py +360 -0
  11. pytransit-2.9.0/pytransit/utils/io/__init__.py +0 -0
  12. pytransit-2.9.0/pytransit/utils/io/lightcurvedata.py +0 -22
  13. {pytransit-2.9.0 → pytransit-2.9.1}/LICENSE +0 -0
  14. {pytransit-2.9.0 → pytransit-2.9.1}/MANIFEST.in +0 -0
  15. {pytransit-2.9.0 → pytransit-2.9.1}/PyTransit.egg-info/dependency_links.txt +0 -0
  16. {pytransit-2.9.0 → pytransit-2.9.1}/PyTransit.egg-info/requires.txt +0 -0
  17. {pytransit-2.9.0 → pytransit-2.9.1}/PyTransit.egg-info/top_level.txt +0 -0
  18. {pytransit-2.9.0 → pytransit-2.9.1}/README.md +0 -0
  19. {pytransit-2.9.0 → pytransit-2.9.1}/pyproject.toml +0 -0
  20. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/contamination/__init__.py +0 -0
  21. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/contamination/contamination.py +0 -0
  22. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/contamination/data/spectra.h5 +0 -0
  23. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/contamination/data/transmission.nc +0 -0
  24. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/contamination/filter.py +0 -0
  25. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/contamination/instrument.py +0 -0
  26. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/contamination/plotting.py +0 -0
  27. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/limb_darkening.py +0 -0
  28. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/lpf/__init__.py +0 -0
  29. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/lpf/baselines/__init__.py +0 -0
  30. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/lpf/baselines/legendrebaseline.py +0 -0
  31. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/lpf/baselines/linearbaseline.py +0 -0
  32. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/lpf/baselines/lstsqbaseline.py +0 -0
  33. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/lpf/cntlpf.py +0 -0
  34. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/lpf/eclipselpf.py +0 -0
  35. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/lpf/loglikelihood/__init__.py +0 -0
  36. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/lpf/loglikelihood/celeriteloglikelihood.py +0 -0
  37. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/lpf/loglikelihood/clloglikelihood.py +0 -0
  38. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/lpf/loglikelihood/fmcloglikelihood.py +0 -0
  39. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/lpf/loglikelihood/logisticloglikelihood.py +0 -0
  40. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/lpf/loglikelihood/multiceleriteloglikelihood.py +0 -0
  41. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/lpf/loglikelihood/wnloglikelihood.py +0 -0
  42. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/lpf/logposteriorfunction.py +0 -0
  43. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/lpf/lpf.py +0 -0
  44. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/lpf/mclpf.py +0 -0
  45. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/lpf/mptlpf.py +0 -0
  46. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/lpf/ocllpf.py +0 -0
  47. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/lpf/ocltdvlpf.py +0 -0
  48. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/lpf/oclttvlpf.py +0 -0
  49. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/lpf/phasecurvelpf.py +0 -0
  50. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/lpf/rvlpf.py +0 -0
  51. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/lpf/tdvlpf.py +0 -0
  52. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/lpf/tess/__init__.py +0 -0
  53. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/lpf/tess/ctesslpf.py +0 -0
  54. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/lpf/tess/tessttvlpf.py +0 -0
  55. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/lpf/tess/tgclpf.py +0 -0
  56. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/lpf/tess/tgcmplpf.py +0 -0
  57. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/lpf/tess/tmpclpf.py +0 -0
  58. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/lpf/tesslpf.py +0 -0
  59. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/lpf/tessoclttvlpf.py +0 -0
  60. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/lpf/transitanalysis.py +0 -0
  61. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/lpf/transitlpf.py +0 -0
  62. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/lpf/ttvlpf.py +0 -0
  63. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/models/__init__.py +0 -0
  64. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/models/eclipse_model.py +0 -0
  65. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/models/gdmodel.py +0 -0
  66. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/models/general.py +0 -0
  67. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/models/ldmodel.py +0 -0
  68. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/models/ldtkldm.py +0 -0
  69. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/models/limb_darkening/__init__.py +0 -0
  70. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/models/limb_darkening/evaluation.py +0 -0
  71. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/models/limb_darkening/exponential.py +0 -0
  72. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/models/limb_darkening/general.py +0 -0
  73. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/models/limb_darkening/linear.py +0 -0
  74. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/models/limb_darkening/logarithmic.py +0 -0
  75. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/models/limb_darkening/nonlinear.py +0 -0
  76. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/models/limb_darkening/power_2.py +0 -0
  77. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/models/limb_darkening/quadratic.py +0 -0
  78. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/models/limb_darkening/quadratic_tri.py +0 -0
  79. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/models/limb_darkening/square_root.py +0 -0
  80. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/models/limb_darkening/uniform.py +0 -0
  81. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/models/ma_chromosphere.py +0 -0
  82. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/models/ma_quadratic.py +0 -0
  83. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/models/ma_quadratic_cl.py +0 -0
  84. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/models/ma_uniform.py +0 -0
  85. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/models/ma_uniform_cl.py +0 -0
  86. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/models/new_eclipse_model.py +0 -0
  87. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/models/numba/__init__.py +0 -0
  88. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/models/numba/gdmodel.py +0 -0
  89. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/models/numba/general_nb.py +0 -0
  90. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/models/numba/ldmodels.py +0 -0
  91. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/models/numba/ldtkldm.py +0 -0
  92. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/models/numba/ma_chromosphere_nb.py +0 -0
  93. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/models/numba/ma_quadratic_nb.py +0 -0
  94. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/models/numba/ma_uniform_nb.py +0 -0
  95. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/models/numba/phasecurves.py +0 -0
  96. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/models/numba/qpower2_nb.py +0 -0
  97. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/models/numba/spmodel.py +0 -0
  98. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/models/numba/udmodel.py +0 -0
  99. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/models/opencl/ma_quadratic.cl +0 -0
  100. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/models/opencl/ma_uniform.cl +0 -0
  101. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/models/opencl/orbits.cl +0 -0
  102. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/models/opencl/qpower2.cl +0 -0
  103. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/models/osmodel.py +0 -0
  104. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/models/qpower2.py +0 -0
  105. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/models/qpower2_cl.py +0 -0
  106. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/models/roadrunner/__init__.py +0 -0
  107. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/models/roadrunner/common.py +0 -0
  108. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/models/roadrunner/ecintersection.py +0 -0
  109. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/models/roadrunner/esmodel.py +0 -0
  110. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/models/roadrunner/model_eclipse.py +0 -0
  111. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/models/roadrunner/model_ecspec.py +0 -0
  112. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/models/roadrunner/model_full.py +0 -0
  113. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/models/roadrunner/model_simple.py +0 -0
  114. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/models/roadrunner/model_trspec.py +0 -0
  115. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/models/roadrunner/opmodel.py +0 -0
  116. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/models/roadrunner/opmodel_full.py +0 -0
  117. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/models/roadrunner/rrmodel.cl +0 -0
  118. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/models/roadrunner/rrmodel.py +0 -0
  119. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/models/roadrunner/rrmodel_cl.py +0 -0
  120. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/models/roadrunner/tsmodel.py +0 -0
  121. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/models/transitmodel.py +0 -0
  122. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/models/uniform.py +0 -0
  123. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/orbits/__init__.py +0 -0
  124. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/orbits/orbits.py +0 -0
  125. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/orbits/orbits_py.py +0 -0
  126. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/orbits/taylor_z.py +0 -0
  127. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/param/__init__.py +0 -0
  128. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/param/parameter.py +0 -0
  129. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/param/prior.py +0 -0
  130. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/param/utilities.py +0 -0
  131. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/stars/__init__.py +0 -0
  132. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/stars/btsettl.py +0 -0
  133. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/stars/data/avg_bt_settl.fits +0 -0
  134. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/stars/data/avg_husser2013.fits +0 -0
  135. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/stars/husser2013.py +0 -0
  136. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/utils/__init__.py +0 -0
  137. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/utils/astrometry.py +0 -0
  138. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/utils/constants.py +0 -0
  139. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/utils/de.py +0 -0
  140. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/utils/downsample.py +0 -0
  141. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/utils/eclipses.py +0 -0
  142. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/utils/keplerlc.py +0 -0
  143. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/utils/mdwarfs.py +0 -0
  144. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/utils/misc.py +0 -0
  145. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/utils/mocklc.py +0 -0
  146. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/utils/octasphere.py +0 -0
  147. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/utils/phasecurves.py +0 -0
  148. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/utils/physics.py +0 -0
  149. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/utils/rv.py +0 -0
  150. {pytransit-2.9.0 → pytransit-2.9.1}/pytransit/utils/tess.py +0 -0
  151. {pytransit-2.9.0 → pytransit-2.9.1}/requirements.txt +0 -0
  152. {pytransit-2.9.0 → pytransit-2.9.1}/setup.cfg +0 -0
  153. {pytransit-2.9.0 → pytransit-2.9.1}/tests/test_contamination.py +0 -0
  154. {pytransit-2.9.0 → pytransit-2.9.1}/tests/test_ecintersection.py +0 -0
  155. {pytransit-2.9.0 → pytransit-2.9.1}/tests/test_limb_darkening.py +0 -0
  156. {pytransit-2.9.0 → pytransit-2.9.1}/tests/test_lstsqbaseline.py +0 -0
  157. {pytransit-2.9.0 → pytransit-2.9.1}/tests/test_ma_quadratic.py +0 -0
  158. {pytransit-2.9.0 → pytransit-2.9.1}/tests/test_ma_quadratic_nb.py +0 -0
  159. {pytransit-2.9.0 → pytransit-2.9.1}/tests/test_opmodel.py +0 -0
  160. {pytransit-2.9.0 → pytransit-2.9.1}/tests/test_qpower2_nb.py +0 -0
  161. {pytransit-2.9.0 → pytransit-2.9.1}/tests/test_roadrunner_eclipse.py +0 -0
  162. {pytransit-2.9.0 → pytransit-2.9.1}/tests/test_rrmodel.py +0 -0
  163. {pytransit-2.9.0 → pytransit-2.9.1}/tests/test_uniform_model.py +0 -0
  164. {pytransit-2.9.0 → pytransit-2.9.1}/tests/test_uniform_model_nb.py +0 -0
  165. {pytransit-2.9.0 → pytransit-2.9.1}/tests/test_z.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: PyTransit
3
- Version: 2.9.0
3
+ Version: 2.9.1
4
4
  Summary: Fast and painless exoplanet transit light curve modelling.
5
5
  Author-email: Hannu Parviainen <hpparvi@gmail.com>
6
6
  License: GPLv2
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: PyTransit
3
- Version: 2.9.0
3
+ Version: 2.9.1
4
4
  Summary: Fast and painless exoplanet transit light curve modelling.
5
5
  Author-email: Hannu Parviainen <hpparvi@gmail.com>
6
6
  License: GPLv2
@@ -141,9 +141,12 @@ pytransit/utils/physics.py
141
141
  pytransit/utils/rv.py
142
142
  pytransit/utils/tess.py
143
143
  pytransit/utils/io/__init__.py
144
- pytransit/utils/io/lightcurvedata.py
144
+ pytransit/utils/io/base.py
145
+ pytransit/utils/io/lcdata.py
146
+ pytransit/utils/io/rvdata.py
145
147
  tests/test_contamination.py
146
148
  tests/test_ecintersection.py
149
+ tests/test_lcdata.py
147
150
  tests/test_limb_darkening.py
148
151
  tests/test_lstsqbaseline.py
149
152
  tests/test_ma_quadratic.py
@@ -152,6 +155,7 @@ tests/test_opmodel.py
152
155
  tests/test_qpower2_nb.py
153
156
  tests/test_roadrunner_eclipse.py
154
157
  tests/test_rrmodel.py
158
+ tests/test_rvdata.py
155
159
  tests/test_uniform_model.py
156
160
  tests/test_uniform_model_nb.py
157
161
  tests/test_z.py
@@ -41,7 +41,7 @@ Date
41
41
 
42
42
  """
43
43
 
44
- __version__ = '2.9.0'
44
+ __version__ = '2.9.1'
45
45
 
46
46
  # Generic
47
47
  # -------
@@ -0,0 +1,20 @@
1
+ # PyTransit: fast and easy exoplanet transit modelling in Python.
2
+ # Copyright (C) 2010-2026 Hannu Parviainen
3
+ #
4
+ # This program is free software: you can redistribute it and/or modify
5
+ # it under the terms of the GNU General Public License as published by
6
+ # the Free Software Foundation, either version 3 of the License, or
7
+ # (at your option) any later version.
8
+ #
9
+ # This program is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU General Public License
15
+ # along with this program. If not, see <https://www.gnu.org/licenses/>.
16
+
17
+ from .lcdata import LCData, LCDataGroup
18
+ from .rvdata import RVData, RVDataGroup
19
+
20
+ __all__ = ['LCData', 'LCDataGroup', 'RVData', 'RVDataGroup']
@@ -0,0 +1,385 @@
1
+ # PyTransit: fast and easy exoplanet transit modelling in Python.
2
+ # Copyright (C) 2010-2026 Hannu Parviainen
3
+ #
4
+ # This program is free software: you can redistribute it and/or modify
5
+ # it under the terms of the GNU General Public License as published by
6
+ # the Free Software Foundation, either version 3 of the License, or
7
+ # (at your option) any later version.
8
+ #
9
+ # This program is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU General Public License
15
+ # along with this program. If not, see <https://www.gnu.org/licenses/>.
16
+
17
+ """Shared machinery for the observation data containers.
18
+
19
+ `LCData`/`LCDataGroup` and `RVData`/`RVDataGroup` differ in the metadata
20
+ they carry but share their field validation and their container behaviour. The validators
21
+ here are plain functions each item class calls in order, so the classes stay flat and
22
+ readable, while `_Data` and `_DataGroup` hold the addition algebra and the container
23
+ protocol.
24
+ """
25
+
26
+ import warnings
27
+
28
+ from collections.abc import Sequence
29
+ from typing import Callable, Optional, Union
30
+
31
+ from numpy import (ndarray, asarray, zeros, array, nan, isfinite, integer, bool_)
32
+
33
+
34
+ # Scalar conversion
35
+ # -----------------
36
+ def _as_float_array(x, name: str) -> ndarray:
37
+ """Convert `x` into a float64 array, re-raising conversion failures informatively."""
38
+ try:
39
+ return asarray(x, 'd')
40
+ except (TypeError, ValueError) as e:
41
+ raise ValueError(f"Could not convert '{name}' into a float array: {e}") from e
42
+
43
+
44
+ def _as_int(v, name: str) -> int:
45
+ """Convert `v` into an int, rejecting non-integral values."""
46
+ if isinstance(v, (bool, bool_)):
47
+ raise ValueError(f"'{name}' must be an integer, got a boolean.")
48
+ try:
49
+ iv = int(v)
50
+ except (TypeError, ValueError) as e:
51
+ raise ValueError(f"'{name}' must be an integer, got {v!r}.") from e
52
+ if iv != v:
53
+ raise ValueError(f"'{name}' must be an integer, got {v!r}.")
54
+ return iv
55
+
56
+
57
+ def _as_float(v, name: str) -> float:
58
+ try:
59
+ return float(v)
60
+ except (TypeError, ValueError) as e:
61
+ raise ValueError(f"'{name}' must be a float, got {v!r}.") from e
62
+
63
+
64
+ # Field validation
65
+ # ----------------
66
+ def _validate_time(time, name: str = 'time') -> ndarray:
67
+ """Return `time` as a finite 1D float64 array."""
68
+ t = _as_float_array(time, name)
69
+ if t.ndim != 1:
70
+ raise ValueError(f"'{name}' must be a 1D array, got a {t.ndim}D one.")
71
+ if not isfinite(t).all():
72
+ raise ValueError(f"'{name}' contains non-finite values.")
73
+ return t
74
+
75
+
76
+ def _validate_series(values, npt: int, name: str) -> ndarray:
77
+ """Return `values` as a 1D float64 array of length `npt`, warning on non-finite values."""
78
+ v = _as_float_array(values, name)
79
+ if v.ndim != 1:
80
+ raise ValueError(f"'{name}' must be a 1D array, got a {v.ndim}D one.")
81
+ if v.size != npt:
82
+ raise ValueError(f"'{name}' has {v.size} points but 'time' has {npt}.")
83
+ if not isfinite(v).all():
84
+ warnings.warn(f"'{name}' contains non-finite values.")
85
+ return v
86
+
87
+
88
+ def _validate_error(error, npt: int, name: str = 'error') -> Optional[ndarray]:
89
+ """Return `error` as a positive 1D float64 array of length `npt`, or None."""
90
+ if error is None:
91
+ return None
92
+ e = _as_float_array(error, name)
93
+ if e.ndim != 1:
94
+ raise ValueError(f"'{name}' must be a 1D array, got a {e.ndim}D one.")
95
+ if e.size != npt:
96
+ raise ValueError(f"'{name}' has {e.size} points but 'time' has {npt}.")
97
+ finite = isfinite(e)
98
+ if not finite.all():
99
+ warnings.warn(f"'{name}' contains non-finite values.")
100
+ if (e[finite] <= 0.0).any():
101
+ raise ValueError(f"'{name}' contains non-positive values.")
102
+ return e
103
+
104
+
105
+ def _validate_covariates(covariates, npt: int, name: str = 'covariates') -> ndarray:
106
+ """Return `covariates` as a 2D float64 array with `npt` rows, empty when None."""
107
+ if covariates is None:
108
+ return zeros((npt, 0))
109
+ cv = _as_float_array(covariates, name)
110
+ if cv.ndim == 1:
111
+ cv = cv.reshape((-1, 1))
112
+ elif cv.ndim != 2:
113
+ raise ValueError(f"'{name}' must be a 1D or 2D array, got a {cv.ndim}D one.")
114
+ if cv.shape[0] != npt:
115
+ raise ValueError(f"'{name}' has {cv.shape[0]} rows but 'time' has {npt} points.")
116
+ return cv
117
+
118
+
119
+ def _validate_names(names, name: str = 'passband') -> tuple:
120
+ """Return `names` as a non-empty tuple of strings."""
121
+ if isinstance(names, str):
122
+ return (names,)
123
+ try:
124
+ out = tuple(str(n) for n in names)
125
+ except TypeError as e:
126
+ raise ValueError(f"'{name}' must be a string or a sequence of strings, "
127
+ f"got {names!r}.") from e
128
+ if len(out) == 0:
129
+ raise ValueError(f"'{name}' cannot be empty.")
130
+ return out
131
+
132
+
133
+ def _validate_pids(pids, name: str = 'pids') -> Optional[tuple]:
134
+ """Return `pids` as a tuple of unique non-negative ints, or None.
135
+
136
+ Only `LCData` uses this: an RV signal always contains every planet in the
137
+ system, so there is no per-dataset subset to name. It lives here to keep the validators
138
+ in one place.
139
+ """
140
+ if pids is None:
141
+ return None
142
+ if isinstance(pids, str):
143
+ raise ValueError(f"'{name}' must be an integer or a sequence of integers, "
144
+ f"got the string {pids!r}.")
145
+ if isinstance(pids, (int, integer)) and not isinstance(pids, (bool, bool_)):
146
+ pids = [pids]
147
+ try:
148
+ pl = tuple(_as_int(p, name) for p in pids)
149
+ except TypeError as e:
150
+ raise ValueError(f"'{name}' must be an integer or a sequence of integers, "
151
+ f"got {pids!r}.") from e
152
+ if any(p < 0 for p in pl):
153
+ raise ValueError(f"'{name}' cannot contain negative planet indices, got {pids!r}.")
154
+ if len(set(pl)) != len(pl):
155
+ raise ValueError(f"'{name}' contains duplicate planet indices: {pids!r}.")
156
+ return pl
157
+
158
+
159
+ class _Data:
160
+ """Addition algebra shared by the single-dataset containers.
161
+
162
+ Subclasses set `_group_type` to their group class once it is defined.
163
+ """
164
+
165
+ _group_type = None
166
+
167
+ def __add__(self, other):
168
+ gt = self._group_type
169
+ if isinstance(other, gt):
170
+ return gt([self] + other.data)
171
+ if isinstance(other, gt._item_type):
172
+ return gt([self, other])
173
+ return NotImplemented
174
+
175
+ def __radd__(self, other):
176
+ if other == 0 or other is None:
177
+ return self._group_type([self])
178
+ return NotImplemented
179
+
180
+
181
+ class _DataGroup:
182
+ """Container protocol and metadata shared by the dataset groups.
183
+
184
+ Subclasses set `_item_type` to the class they hold. The datasets keep their insertion
185
+ order, which defines the dataset index a model will use. Nothing is sorted implicitly.
186
+ """
187
+
188
+ _item_type = None
189
+
190
+ def __init__(self, data=()) -> None:
191
+ self.data: list = []
192
+ if isinstance(data, (self._item_type, _DataGroup)):
193
+ data = [data]
194
+ for d in data:
195
+ self._add_data(d)
196
+
197
+ def _add_data(self, d) -> None:
198
+ if isinstance(d, _DataGroup):
199
+ for x in d.data:
200
+ self._add_data(x)
201
+ return
202
+ if not isinstance(d, self._item_type):
203
+ raise TypeError(f"A {type(self).__name__} holds {self._item_type.__name__} objects, "
204
+ f"got {type(d).__name__}.")
205
+ if any(d is x for x in self.data):
206
+ raise ValueError(f"The same {self._item_type.__name__} instance cannot be added "
207
+ f"to a group twice.")
208
+ self.data.append(d)
209
+
210
+ # Bulk data
211
+ # ---------
212
+ @property
213
+ def times(self) -> list:
214
+ """List of 1D time arrays."""
215
+ return [d.time for d in self.data]
216
+
217
+ @property
218
+ def covariates(self) -> list:
219
+ """List of 2D covariate matrices."""
220
+ return [d.covariates for d in self.data]
221
+
222
+ @property
223
+ def errors(self) -> list:
224
+ """List of 1D uncertainty arrays."""
225
+ return [d.error for d in self.data]
226
+
227
+ # Metadata
228
+ # --------
229
+ @property
230
+ def instruments(self) -> list:
231
+ """List of instrument names."""
232
+ return [d.instrument for d in self.data]
233
+
234
+ # Sizes
235
+ # -----
236
+ @property
237
+ def size(self) -> int:
238
+ """Number of datasets."""
239
+ return len(self.data)
240
+
241
+ @property
242
+ def npts(self) -> ndarray:
243
+ """Array of per-dataset datapoint counts."""
244
+ return array([d.size for d in self.data], int)
245
+
246
+ @property
247
+ def ncovs(self) -> ndarray:
248
+ """Array of per-dataset covariate counts."""
249
+ return array([d.ncov for d in self.data], int)
250
+
251
+ @property
252
+ def has_errors(self) -> bool:
253
+ """True if every dataset was given an explicit uncertainty array."""
254
+ return all(d.has_error for d in self.data) if self.data else False
255
+
256
+ @property
257
+ def has_covariates(self) -> bool:
258
+ """True if any dataset has at least one covariate."""
259
+ return bool((self.ncovs > 0).any())
260
+
261
+ @property
262
+ def tmin(self) -> float:
263
+ """Earliest time in the group."""
264
+ return min((d.time.min() for d in self.data if d.size), default=nan)
265
+
266
+ @property
267
+ def tmax(self) -> float:
268
+ """Latest time in the group."""
269
+ return max((d.time.max() for d in self.data if d.size), default=nan)
270
+
271
+ # Derived ids
272
+ # -----------
273
+ def _group_ids(self, *fields) -> ndarray:
274
+ """Map tuples of per-dataset metadata into 0-based contiguous group ids.
275
+
276
+ The ids are numbered by first appearance, so they are contiguous by construction,
277
+ which `BaseLPF._init_data` asserts for the noise ids.
278
+ """
279
+ keys = list(zip(*fields))
280
+ if not keys:
281
+ return zeros(0, int)
282
+ cats = {k: i for i, k in enumerate(dict.fromkeys(keys))}
283
+ return array([cats[k] for k in keys], int)
284
+
285
+ @property
286
+ def wnids(self) -> ndarray:
287
+ """Noise block index per dataset, grouping the datasets by instrument."""
288
+ return self._group_ids(self.instruments)
289
+
290
+ @property
291
+ def ins(self) -> list:
292
+ """Instrument names, under the name `LinearModelBaseline` looks for."""
293
+ return self.instruments
294
+
295
+ @property
296
+ def piis(self) -> ndarray:
297
+ """Running index of each dataset within its instrument."""
298
+ counts: dict = {}
299
+ out = []
300
+ for ins in self.instruments:
301
+ out.append(counts.get(ins, 0))
302
+ counts[ins] = out[-1] + 1
303
+ return array(out, int)
304
+
305
+ # Selection
306
+ # ---------
307
+ def select(self, **criteria) -> '_DataGroup':
308
+ """Return a new group with the datasets matching all the given criteria.
309
+
310
+ Each keyword names an attribute of the contained objects. A tuple-valued attribute
311
+ such as `passband` or `pids` matches by membership, everything else by equality.
312
+ Criteria given as `None` are ignored.
313
+ """
314
+ def matches(d) -> bool:
315
+ for k, v in criteria.items():
316
+ if v is None:
317
+ continue
318
+ if not hasattr(d, k):
319
+ raise ValueError(f"Unknown selection criterion {k!r} for "
320
+ f"{self._item_type.__name__}.")
321
+ a = getattr(d, k)
322
+ if isinstance(a, tuple):
323
+ if v not in a:
324
+ return False
325
+ elif a != v:
326
+ return False
327
+ return True
328
+ return type(self)([d for d in self.data if matches(d)])
329
+
330
+ def sorted_by(self, key: Union[str, Callable] = 'time') -> '_DataGroup':
331
+ """Return a new group ordered by `key`.
332
+
333
+ Parameters
334
+ ----------
335
+ key
336
+ 'time', the name of any attribute of the contained objects, or a callable
337
+ taking one of them and returning a sort key.
338
+ """
339
+ if callable(key):
340
+ f = key
341
+ elif key == 'time':
342
+ f = lambda d: d.time.min() if d.size else nan
343
+ else:
344
+ if not self.data:
345
+ return type(self)()
346
+ if not hasattr(self.data[0], key):
347
+ raise ValueError(f"Unknown sort key {key!r}, choose 'time', an attribute of "
348
+ f"{self._item_type.__name__}, or pass a callable.")
349
+ f = lambda d: getattr(d, key)
350
+ return type(self)(sorted(self.data, key=f))
351
+
352
+ # Container protocol
353
+ # ------------------
354
+ def __len__(self) -> int:
355
+ return self.size
356
+
357
+ def __iter__(self):
358
+ return iter(self.data)
359
+
360
+ def __getitem__(self, index):
361
+ if isinstance(index, (int, integer)):
362
+ return self.data[index]
363
+ if isinstance(index, slice):
364
+ return type(self)(self.data[index])
365
+ ix = asarray(index)
366
+ if ix.dtype == bool:
367
+ if ix.size != self.size:
368
+ raise IndexError(f"Boolean index has {ix.size} entries but the group has "
369
+ f"{self.size} datasets.")
370
+ return type(self)([d for d, m in zip(self.data, ix) if m])
371
+ if ix.dtype.kind in 'iu':
372
+ return type(self)([self.data[i] for i in ix])
373
+ raise TypeError(f"Cannot index a {type(self).__name__} with {type(index).__name__}.")
374
+
375
+ def __add__(self, other):
376
+ if isinstance(other, type(self)):
377
+ return type(self)(self.data + other.data)
378
+ if isinstance(other, self._item_type):
379
+ return type(self)(self.data + [other])
380
+ return NotImplemented
381
+
382
+ def __radd__(self, other):
383
+ if other == 0 or other is None:
384
+ return type(self)(self.data)
385
+ return NotImplemented