orblet 0.1.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.
- orblet-0.1.0/LICENSE +21 -0
- orblet-0.1.0/PKG-INFO +140 -0
- orblet-0.1.0/README.md +100 -0
- orblet-0.1.0/pyproject.toml +70 -0
- orblet-0.1.0/setup.cfg +4 -0
- orblet-0.1.0/src/orblet/__init__.py +207 -0
- orblet-0.1.0/src/orblet/chain_stats.py +194 -0
- orblet-0.1.0/src/orblet/constants.py +307 -0
- orblet-0.1.0/src/orblet/design/__init__.py +53 -0
- orblet-0.1.0/src/orblet/design/builder.py +461 -0
- orblet-0.1.0/src/orblet/design/columns.py +453 -0
- orblet-0.1.0/src/orblet/elements.py +934 -0
- orblet-0.1.0/src/orblet/interpret/__init__.py +20 -0
- orblet-0.1.0/src/orblet/interpret/amrf.py +1178 -0
- orblet-0.1.0/src/orblet/interpret/astrometric_upper_limit.py +262 -0
- orblet-0.1.0/src/orblet/interpret/companion_mass.py +758 -0
- orblet-0.1.0/src/orblet/interpret/flux_ratio.py +724 -0
- orblet-0.1.0/src/orblet/kepler.py +222 -0
- orblet-0.1.0/src/orblet/likelihood.py +335 -0
- orblet-0.1.0/src/orblet/model.py +1082 -0
- orblet-0.1.0/src/orblet/parallax.py +258 -0
- orblet-0.1.0/src/orblet/periodogram.py +875 -0
- orblet-0.1.0/src/orblet/plotting.py +1315 -0
- orblet-0.1.0/src/orblet/prepare.py +370 -0
- orblet-0.1.0/src/orblet/priors.py +715 -0
- orblet-0.1.0/src/orblet/residuals.py +225 -0
- orblet-0.1.0/src/orblet/rv_chain.py +346 -0
- orblet-0.1.0/src/orblet/sampling.py +544 -0
- orblet-0.1.0/src/orblet/search.py +686 -0
- orblet-0.1.0/src/orblet/simulate/__init__.py +19 -0
- orblet-0.1.0/src/orblet/simulate/bundles.py +277 -0
- orblet-0.1.0/src/orblet/simulate/cadence.py +331 -0
- orblet-0.1.0/src/orblet/simulate/orbit.py +470 -0
- orblet-0.1.0/src/orblet/solve/__init__.py +24 -0
- orblet-0.1.0/src/orblet/solve/astrometry.py +1020 -0
- orblet-0.1.0/src/orblet/solve/rv.py +799 -0
- orblet-0.1.0/src/orblet.egg-info/PKG-INFO +140 -0
- orblet-0.1.0/src/orblet.egg-info/SOURCES.txt +66 -0
- orblet-0.1.0/src/orblet.egg-info/dependency_links.txt +1 -0
- orblet-0.1.0/src/orblet.egg-info/requires.txt +26 -0
- orblet-0.1.0/src/orblet.egg-info/top_level.txt +1 -0
- orblet-0.1.0/tests/test_a0_independent_orbit_oracle.py +720 -0
- orblet-0.1.0/tests/test_a0_joint_oracle.py +567 -0
- orblet-0.1.0/tests/test_a0_joint_twoamp_oracle.py +1034 -0
- orblet-0.1.0/tests/test_campbell_primary_frame_noop.py +214 -0
- orblet-0.1.0/tests/test_companion_mass_core.py +91 -0
- orblet-0.1.0/tests/test_design_builder_equivalence_rv.py +57 -0
- orblet-0.1.0/tests/test_design_builder_equivalence_ti.py +86 -0
- orblet-0.1.0/tests/test_design_builder_protocol.py +113 -0
- orblet-0.1.0/tests/test_design_builder_singlestar_block.py +66 -0
- orblet-0.1.0/tests/test_design_columns_baseline.py +147 -0
- orblet-0.1.0/tests/test_forward_model_baseline.py +157 -0
- orblet-0.1.0/tests/test_joint_epoch_contract.py +155 -0
- orblet-0.1.0/tests/test_kepler.py +301 -0
- orblet-0.1.0/tests/test_kepler_basis_tperi_contract.py +71 -0
- orblet-0.1.0/tests/test_likelihood_baseline.py +118 -0
- orblet-0.1.0/tests/test_manual_doc_anchors.py +206 -0
- orblet-0.1.0/tests/test_obs_time_j2010_invariant.py +43 -0
- orblet-0.1.0/tests/test_orbit_plotting.py +201 -0
- orblet-0.1.0/tests/test_orblet_optional_dependencies.py +336 -0
- orblet-0.1.0/tests/test_packaging.py +152 -0
- orblet-0.1.0/tests/test_pdc.py +301 -0
- orblet-0.1.0/tests/test_postprocess.py +493 -0
- orblet-0.1.0/tests/test_prior_spec_parser_contract.py +146 -0
- orblet-0.1.0/tests/test_priors.py +67 -0
- orblet-0.1.0/tests/test_public_composable_api.py +199 -0
- orblet-0.1.0/tests/test_rv_sign_convention.py +99 -0
- orblet-0.1.0/tests/test_simulator_python_roundtrip.py +541 -0
orblet-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Sahar Shahaf
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
orblet-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: orblet
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Atoms for Keplerian orbit analysis: forward models, likelihoods, design matrices and linear solves, period search, element conversion. Arrays in, results out.
|
|
5
|
+
Author-email: Sahar Shahaf <sahar.shahaf@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Repository, https://github.com/saharsh1/orblet
|
|
8
|
+
Project-URL: Issues, https://github.com/saharsh1/orblet/issues
|
|
9
|
+
Keywords: astronomy,binary-stars,orbits,astrometry,radial-velocity,kepler
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Intended Audience :: Science/Research
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Topic :: Scientific/Engineering :: Astronomy
|
|
16
|
+
Requires-Python: <3.13,>=3.11
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
License-File: LICENSE
|
|
19
|
+
Requires-Dist: numpy<3,>=2
|
|
20
|
+
Requires-Dist: scipy<2,>=1.14
|
|
21
|
+
Requires-Dist: astropy<8,>=7
|
|
22
|
+
Requires-Dist: matplotlib<4,>=3.8
|
|
23
|
+
Provides-Extra: sampling
|
|
24
|
+
Requires-Dist: emcee<4,>=3.1; extra == "sampling"
|
|
25
|
+
Provides-Extra: plots
|
|
26
|
+
Requires-Dist: corner<3,>=2.2; extra == "plots"
|
|
27
|
+
Provides-Extra: tables
|
|
28
|
+
Requires-Dist: pandas<4,>=2; extra == "tables"
|
|
29
|
+
Provides-Extra: progress
|
|
30
|
+
Requires-Dist: tqdm>=4.60; extra == "progress"
|
|
31
|
+
Provides-Extra: all
|
|
32
|
+
Requires-Dist: emcee<4,>=3.1; extra == "all"
|
|
33
|
+
Requires-Dist: corner<3,>=2.2; extra == "all"
|
|
34
|
+
Requires-Dist: pandas<4,>=2; extra == "all"
|
|
35
|
+
Requires-Dist: tqdm>=4.60; extra == "all"
|
|
36
|
+
Provides-Extra: dev
|
|
37
|
+
Requires-Dist: pytest>=7; extra == "dev"
|
|
38
|
+
Requires-Dist: build>=1.0; extra == "dev"
|
|
39
|
+
Dynamic: license-file
|
|
40
|
+
|
|
41
|
+
# orblet <img src="https://raw.githubusercontent.com/saharsh1/orblet/main/docs/assets/orblet.png" alt="orblet" height="44" align="right">
|
|
42
|
+
|
|
43
|
+
Atoms for Keplerian orbit analysis. Forward models, likelihoods, design
|
|
44
|
+
matrices and linear solves, period search, element conversion, plotting —
|
|
45
|
+
each a function that takes arrays and returns a result, with its units,
|
|
46
|
+
frames and assumptions stated in its docstring.
|
|
47
|
+
|
|
48
|
+
orblet is **not a solver**. It does not know what a Gaia epoch is, does not
|
|
49
|
+
read files, and does not contact the network. You build the pipeline; orblet
|
|
50
|
+
supplies the pieces.
|
|
51
|
+
|
|
52
|
+
```python
|
|
53
|
+
from orblet import ti_design_matrix, linear_solve_ti, ti_to_kepler
|
|
54
|
+
|
|
55
|
+
X = ti_design_matrix(t_mjd, psi_rad, parallax_factor_al, P_days, e, tau, epoch_ref_mjd)
|
|
56
|
+
fit = linear_solve_ti(along_scan_mas, sigma_mas, X)
|
|
57
|
+
elements = ti_to_kepler({"A": fit.beta[0], "B": fit.beta[1], "F": fit.beta[2], "G": fit.beta[3]})
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Install
|
|
61
|
+
|
|
62
|
+
orblet is not on PyPI yet. Install it from the repository:
|
|
63
|
+
|
|
64
|
+
```
|
|
65
|
+
pip install "orblet[all] @ git+https://github.com/saharsh1/orblet.git"
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
or from a local clone, which is the right choice while you are editing it:
|
|
69
|
+
|
|
70
|
+
```
|
|
71
|
+
git clone https://github.com/saharsh1/orblet.git
|
|
72
|
+
pip install -e "./orblet[all]"
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Drop `[all]` for the four core dependencies only — numpy, scipy, astropy,
|
|
76
|
+
matplotlib — which is all a fresh install needs to run the atoms and the
|
|
77
|
+
quickstarts. The extras (`emcee`, `corner`, `pandas`, `tqdm`; also available
|
|
78
|
+
one at a time as `[sampling]`, `[plots]`, `[tables]`, `[progress]`) are each
|
|
79
|
+
imported inside the one function that uses them, so a missing extra fails
|
|
80
|
+
that call with a message naming it and leaves everything else working.
|
|
81
|
+
|
|
82
|
+
## What is here
|
|
83
|
+
|
|
84
|
+
| | |
|
|
85
|
+
|---|---|
|
|
86
|
+
| `orblet.model` | forward models: RV curve, along-scan astrometry, Thiele-Innes and Campbell photocentre orbits |
|
|
87
|
+
| `orblet.likelihood` | Gaussian log-likelihoods with jitter, per channel |
|
|
88
|
+
| `orblet.kepler` | the Kepler-equation solver |
|
|
89
|
+
| `orblet.design` / `orblet.solve` | design-matrix builders and the generalised-least-squares linear solves for RV and astrometry |
|
|
90
|
+
| `orblet.search` / `orblet.periodogram` | the Thiele-Innes frequency scan; Lomb-Scargle and phase-distance-correlation periodograms |
|
|
91
|
+
| `orblet.elements` | Thiele-Innes → Campbell, NSS convention, element extraction from chains |
|
|
92
|
+
| `orblet.priors` | prior classes and the log-prior composer |
|
|
93
|
+
| `orblet.sampling` / `orblet.chain_stats` | emcee helpers; quantiles and circular summaries of chains |
|
|
94
|
+
| `orblet.interpret` | companion mass and the astrometric mass-ratio function |
|
|
95
|
+
| `orblet.simulate` | a synthetic-orbit simulator and a parallax-consistent cadence, for tests and tutorials |
|
|
96
|
+
| `orblet.parallax` | per-direction parallax factors from a DE432s ephemeris |
|
|
97
|
+
| `orblet.plotting` | orbit, residual, sky-overlay and corner plots |
|
|
98
|
+
|
|
99
|
+
The front door — `from orblet import <name>` — exposes 35 names and imports
|
|
100
|
+
nothing heavy: `import orblet` pulls in no scipy, no matplotlib, no astropy.
|
|
101
|
+
Each name resolves on first use.
|
|
102
|
+
|
|
103
|
+
## Conventions
|
|
104
|
+
|
|
105
|
+
Every public function states them in its docstring. The ones that bite:
|
|
106
|
+
|
|
107
|
+
- radial velocity: positive is receding; `omega` is the **primary's** argument of periastron
|
|
108
|
+
- period in **days** at the public surface (Keplerian years only inside the Kepler solver)
|
|
109
|
+
- `tau` is the periastron phase in `[0, 1)`; `tp = tau * P + epoch_ref_mjd`
|
|
110
|
+
- astrometric amplitudes `A, B, F, G` are **photocentre** amplitudes in mas, positive-amplitude convention; the parallax term enters as `parallax_mas * parallax_factor_al`, additive
|
|
111
|
+
- Gaia's `pmra` is already `mu_alpha*` — never apply `cos(dec)` again
|
|
112
|
+
- **a seed is initialisation, never a prior**: a starting point for a sampler carries no evidence
|
|
113
|
+
|
|
114
|
+
## Four laws, each a test
|
|
115
|
+
|
|
116
|
+
1. A public name **is** the object it claims to be — no silent wrappers.
|
|
117
|
+
2. The numerical core does not move: byte-identity baselines pin the forward
|
|
118
|
+
models, the likelihoods and the design columns.
|
|
119
|
+
3. Conventions hold: RV sign, primary-frame ω, τ → tp, time scales.
|
|
120
|
+
4. orblet imports nothing outside itself, the four dependencies and the
|
|
121
|
+
standard library.
|
|
122
|
+
|
|
123
|
+
## Status
|
|
124
|
+
|
|
125
|
+
Grew inside the `gaia-orbits` repository until 2026-09-24; developed here
|
|
126
|
+
since, and consumed there as an installed dependency.
|
|
127
|
+
|
|
128
|
+
Three simulator presets ship. `OrbitSimulator.bh1_like()` is the one the
|
|
129
|
+
quickstarts use, through `load_simulated_inputs(scenario="bh1")`: an
|
|
130
|
+
illustrative orbit on an invented sky position, with a cadence whose parallax
|
|
131
|
+
factor is coupled to that position, so injecting and removing the parallax
|
|
132
|
+
signal use the same array and closure is exact. `toy_orbit()` is a compact
|
|
133
|
+
short-period orbit for quick smoke tests. `bh3_like()` carries orbital numbers
|
|
134
|
+
rounded from a published system and is useful for a realistic-looking truth;
|
|
135
|
+
pair it with `DemoParallaxConsistentCadence` for anything closure-grade. The
|
|
136
|
+
`scenario=` parameter and the `bh1`/`toy` naming are slated to be simplified.
|
|
137
|
+
|
|
138
|
+
## Licence
|
|
139
|
+
|
|
140
|
+
MIT.
|
orblet-0.1.0/README.md
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# orblet <img src="https://raw.githubusercontent.com/saharsh1/orblet/main/docs/assets/orblet.png" alt="orblet" height="44" align="right">
|
|
2
|
+
|
|
3
|
+
Atoms for Keplerian orbit analysis. Forward models, likelihoods, design
|
|
4
|
+
matrices and linear solves, period search, element conversion, plotting —
|
|
5
|
+
each a function that takes arrays and returns a result, with its units,
|
|
6
|
+
frames and assumptions stated in its docstring.
|
|
7
|
+
|
|
8
|
+
orblet is **not a solver**. It does not know what a Gaia epoch is, does not
|
|
9
|
+
read files, and does not contact the network. You build the pipeline; orblet
|
|
10
|
+
supplies the pieces.
|
|
11
|
+
|
|
12
|
+
```python
|
|
13
|
+
from orblet import ti_design_matrix, linear_solve_ti, ti_to_kepler
|
|
14
|
+
|
|
15
|
+
X = ti_design_matrix(t_mjd, psi_rad, parallax_factor_al, P_days, e, tau, epoch_ref_mjd)
|
|
16
|
+
fit = linear_solve_ti(along_scan_mas, sigma_mas, X)
|
|
17
|
+
elements = ti_to_kepler({"A": fit.beta[0], "B": fit.beta[1], "F": fit.beta[2], "G": fit.beta[3]})
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Install
|
|
21
|
+
|
|
22
|
+
orblet is not on PyPI yet. Install it from the repository:
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
pip install "orblet[all] @ git+https://github.com/saharsh1/orblet.git"
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
or from a local clone, which is the right choice while you are editing it:
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
git clone https://github.com/saharsh1/orblet.git
|
|
32
|
+
pip install -e "./orblet[all]"
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Drop `[all]` for the four core dependencies only — numpy, scipy, astropy,
|
|
36
|
+
matplotlib — which is all a fresh install needs to run the atoms and the
|
|
37
|
+
quickstarts. The extras (`emcee`, `corner`, `pandas`, `tqdm`; also available
|
|
38
|
+
one at a time as `[sampling]`, `[plots]`, `[tables]`, `[progress]`) are each
|
|
39
|
+
imported inside the one function that uses them, so a missing extra fails
|
|
40
|
+
that call with a message naming it and leaves everything else working.
|
|
41
|
+
|
|
42
|
+
## What is here
|
|
43
|
+
|
|
44
|
+
| | |
|
|
45
|
+
|---|---|
|
|
46
|
+
| `orblet.model` | forward models: RV curve, along-scan astrometry, Thiele-Innes and Campbell photocentre orbits |
|
|
47
|
+
| `orblet.likelihood` | Gaussian log-likelihoods with jitter, per channel |
|
|
48
|
+
| `orblet.kepler` | the Kepler-equation solver |
|
|
49
|
+
| `orblet.design` / `orblet.solve` | design-matrix builders and the generalised-least-squares linear solves for RV and astrometry |
|
|
50
|
+
| `orblet.search` / `orblet.periodogram` | the Thiele-Innes frequency scan; Lomb-Scargle and phase-distance-correlation periodograms |
|
|
51
|
+
| `orblet.elements` | Thiele-Innes → Campbell, NSS convention, element extraction from chains |
|
|
52
|
+
| `orblet.priors` | prior classes and the log-prior composer |
|
|
53
|
+
| `orblet.sampling` / `orblet.chain_stats` | emcee helpers; quantiles and circular summaries of chains |
|
|
54
|
+
| `orblet.interpret` | companion mass and the astrometric mass-ratio function |
|
|
55
|
+
| `orblet.simulate` | a synthetic-orbit simulator and a parallax-consistent cadence, for tests and tutorials |
|
|
56
|
+
| `orblet.parallax` | per-direction parallax factors from a DE432s ephemeris |
|
|
57
|
+
| `orblet.plotting` | orbit, residual, sky-overlay and corner plots |
|
|
58
|
+
|
|
59
|
+
The front door — `from orblet import <name>` — exposes 35 names and imports
|
|
60
|
+
nothing heavy: `import orblet` pulls in no scipy, no matplotlib, no astropy.
|
|
61
|
+
Each name resolves on first use.
|
|
62
|
+
|
|
63
|
+
## Conventions
|
|
64
|
+
|
|
65
|
+
Every public function states them in its docstring. The ones that bite:
|
|
66
|
+
|
|
67
|
+
- radial velocity: positive is receding; `omega` is the **primary's** argument of periastron
|
|
68
|
+
- period in **days** at the public surface (Keplerian years only inside the Kepler solver)
|
|
69
|
+
- `tau` is the periastron phase in `[0, 1)`; `tp = tau * P + epoch_ref_mjd`
|
|
70
|
+
- astrometric amplitudes `A, B, F, G` are **photocentre** amplitudes in mas, positive-amplitude convention; the parallax term enters as `parallax_mas * parallax_factor_al`, additive
|
|
71
|
+
- Gaia's `pmra` is already `mu_alpha*` — never apply `cos(dec)` again
|
|
72
|
+
- **a seed is initialisation, never a prior**: a starting point for a sampler carries no evidence
|
|
73
|
+
|
|
74
|
+
## Four laws, each a test
|
|
75
|
+
|
|
76
|
+
1. A public name **is** the object it claims to be — no silent wrappers.
|
|
77
|
+
2. The numerical core does not move: byte-identity baselines pin the forward
|
|
78
|
+
models, the likelihoods and the design columns.
|
|
79
|
+
3. Conventions hold: RV sign, primary-frame ω, τ → tp, time scales.
|
|
80
|
+
4. orblet imports nothing outside itself, the four dependencies and the
|
|
81
|
+
standard library.
|
|
82
|
+
|
|
83
|
+
## Status
|
|
84
|
+
|
|
85
|
+
Grew inside the `gaia-orbits` repository until 2026-09-24; developed here
|
|
86
|
+
since, and consumed there as an installed dependency.
|
|
87
|
+
|
|
88
|
+
Three simulator presets ship. `OrbitSimulator.bh1_like()` is the one the
|
|
89
|
+
quickstarts use, through `load_simulated_inputs(scenario="bh1")`: an
|
|
90
|
+
illustrative orbit on an invented sky position, with a cadence whose parallax
|
|
91
|
+
factor is coupled to that position, so injecting and removing the parallax
|
|
92
|
+
signal use the same array and closure is exact. `toy_orbit()` is a compact
|
|
93
|
+
short-period orbit for quick smoke tests. `bh3_like()` carries orbital numbers
|
|
94
|
+
rounded from a published system and is useful for a realistic-looking truth;
|
|
95
|
+
pair it with `DemoParallaxConsistentCadence` for anything closure-grade. The
|
|
96
|
+
`scenario=` parameter and the `bh1`/`toy` naming are slated to be simplified.
|
|
97
|
+
|
|
98
|
+
## Licence
|
|
99
|
+
|
|
100
|
+
MIT.
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=77"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "orblet"
|
|
7
|
+
dynamic = ["version"] # read from orblet.__version__, the single source
|
|
8
|
+
description = "Atoms for Keplerian orbit analysis: forward models, likelihoods, design matrices and linear solves, period search, element conversion. Arrays in, results out."
|
|
9
|
+
authors = [{name = "Sahar Shahaf", email = "sahar.shahaf@gmail.com"}]
|
|
10
|
+
readme = "README.md"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
requires-python = ">=3.11,<3.13"
|
|
13
|
+
keywords = ["astronomy", "binary-stars", "orbits", "astrometry", "radial-velocity", "kepler"]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Development Status :: 3 - Alpha",
|
|
16
|
+
"Intended Audience :: Science/Research",
|
|
17
|
+
"Programming Language :: Python :: 3",
|
|
18
|
+
"Programming Language :: Python :: 3.11",
|
|
19
|
+
"Programming Language :: Python :: 3.12",
|
|
20
|
+
"Topic :: Scientific/Engineering :: Astronomy",
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
# The four. Everything in orblet imports at module scope from these and the
|
|
24
|
+
# standard library only; tests/test_orblet_optional_dependencies.py enforces it.
|
|
25
|
+
dependencies = [
|
|
26
|
+
"numpy>=2,<3",
|
|
27
|
+
"scipy>=1.14,<2",
|
|
28
|
+
"astropy>=7,<8",
|
|
29
|
+
"matplotlib>=3.8,<4",
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
[project.urls]
|
|
33
|
+
Repository = "https://github.com/saharsh1/orblet"
|
|
34
|
+
Issues = "https://github.com/saharsh1/orblet/issues"
|
|
35
|
+
|
|
36
|
+
# Optional, each imported inside the one function that needs it, so the
|
|
37
|
+
# package imports and its core paths run without any of them installed.
|
|
38
|
+
[project.optional-dependencies]
|
|
39
|
+
sampling = ["emcee>=3.1,<4"] # orblet.sampling.run_emcee_chains
|
|
40
|
+
plots = ["corner>=2.2,<3"] # orblet.plotting.plot_orbit_corner
|
|
41
|
+
tables = ["pandas>=2,<4"] # orblet.chain_stats.chain_summary_table
|
|
42
|
+
progress = ["tqdm>=4.60"] # a progress bar over batch periodograms
|
|
43
|
+
all = [
|
|
44
|
+
"emcee>=3.1,<4",
|
|
45
|
+
"corner>=2.2,<3",
|
|
46
|
+
"pandas>=2,<4",
|
|
47
|
+
"tqdm>=4.60",
|
|
48
|
+
]
|
|
49
|
+
dev = [
|
|
50
|
+
"pytest>=7",
|
|
51
|
+
"build>=1.0",
|
|
52
|
+
]
|
|
53
|
+
|
|
54
|
+
[tool.setuptools.dynamic]
|
|
55
|
+
version = {attr = "orblet.__version__"}
|
|
56
|
+
|
|
57
|
+
[tool.setuptools.packages.find]
|
|
58
|
+
where = ["src"]
|
|
59
|
+
include = ["orblet*"]
|
|
60
|
+
|
|
61
|
+
[tool.pytest.ini_options]
|
|
62
|
+
testpaths = ["tests"]
|
|
63
|
+
# Same opt-in contract as the parent project: plain `pytest` runs the fast
|
|
64
|
+
# synthetic suite; `pytest -m slow` and `pytest -m benchmark` are explicit.
|
|
65
|
+
addopts = "-m 'not slow and not benchmark'"
|
|
66
|
+
markers = [
|
|
67
|
+
"slow: synthetic MCMC and closure tests; opt-in",
|
|
68
|
+
"coverage: calibration gates over many noise seeds; a subset of slow",
|
|
69
|
+
"benchmark: synthetic-data closure tests; opt-in",
|
|
70
|
+
]
|
orblet-0.1.0/setup.cfg
ADDED
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
"""orblet — a standalone orbit fitter: arrays in, orbital elements out.
|
|
2
|
+
|
|
3
|
+
No mission, no data files, no network. orblet is a CONSUMER of data and never
|
|
4
|
+
a producer: you hand it epochs, measurements and uncertainties, and it hands
|
|
5
|
+
back a model, a likelihood, a solution. Where those arrays came from, and
|
|
6
|
+
whether you are allowed to have them, is not orblet's business.
|
|
7
|
+
|
|
8
|
+
Work the open way
|
|
9
|
+
-----------------
|
|
10
|
+
orblet ships ATOMS, not a black box. A Kepler solver, forward models, design
|
|
11
|
+
matrices, the two linear cores, proposed likelihoods, default priors, a
|
|
12
|
+
frequency search, the element conversions, the plots. You assemble the
|
|
13
|
+
optimiser or sampler you actually want and plug your own target function into
|
|
14
|
+
it; the default recipes are written out in the manuals and the example
|
|
15
|
+
notebooks rather than hidden inside a wrapper.
|
|
16
|
+
|
|
17
|
+
That is a deliberate trade. A one-line `fit()` is quicker to call and harder
|
|
18
|
+
to trust: when it disagrees with your expectation you cannot see which step
|
|
19
|
+
disagreed. Here every step is a function you can call, print and plot on its
|
|
20
|
+
own.
|
|
21
|
+
|
|
22
|
+
The shape of the thing
|
|
23
|
+
----------------------
|
|
24
|
+
model the forward models: elements and epochs -> observables
|
|
25
|
+
design the design matrices, one column per linear parameter
|
|
26
|
+
solve the linear cores — fix the geometry, solve the rest exactly
|
|
27
|
+
search the frequency scan and the periodograms
|
|
28
|
+
likelihood proposed likelihoods (Gaussian with jitter), not mandates
|
|
29
|
+
priors prior classes, defaults, and the spec parser
|
|
30
|
+
prepare epoch and time-scale handling
|
|
31
|
+
sampling MCMC atoms: chains, stacking, R-hat, ESS, dead chains
|
|
32
|
+
elements conversions between parameter conventions
|
|
33
|
+
residuals the 5-parameter residual routine
|
|
34
|
+
parallax observer position -> on-sky parallax factors
|
|
35
|
+
interpret what a fitted orbit implies: masses, limits, flux ratios
|
|
36
|
+
plotting the orbit, the corner, the sky plane
|
|
37
|
+
simulate synthetic data with known truth, for testing and teaching
|
|
38
|
+
|
|
39
|
+
Why the split matters: the geometry of a Keplerian orbit is non-linear, but
|
|
40
|
+
once it is fixed the amplitudes enter LINEARLY and have a closed-form best
|
|
41
|
+
fit. `solve` is that closed form. A sampler over a handful of non-linear
|
|
42
|
+
parameters, with `solve` as its inner step, beats one over all of them.
|
|
43
|
+
|
|
44
|
+
Import cost
|
|
45
|
+
-----------
|
|
46
|
+
Nothing heavy is imported when you `import orblet`. Every name below is
|
|
47
|
+
resolved on first attribute access (PEP 562), so scipy, emcee and matplotlib
|
|
48
|
+
arrive only if you touch something that needs them.
|
|
49
|
+
|
|
50
|
+
**This package is under construction.** It is being assembled in place inside
|
|
51
|
+
the gaia-orbits monorepo, one dependency layer at a time, before being
|
|
52
|
+
extracted to its own repository. The modules arrive by relocation, unchanged.
|
|
53
|
+
|
|
54
|
+
State of the move: `docs/plans/2026-09-23-orblet-b1-chunks.md`
|
|
55
|
+
Plan of record: `docs/plans/2026-09-23-orblet-extraction-plan.md`
|
|
56
|
+
Machine-checked: `tests/_orblet_move_map.py`
|
|
57
|
+
"""
|
|
58
|
+
|
|
59
|
+
from __future__ import annotations
|
|
60
|
+
|
|
61
|
+
import importlib
|
|
62
|
+
|
|
63
|
+
# public name -> (module, attribute). One mechanism, one table, grouped by
|
|
64
|
+
# what the name is FOR rather than by which file it happens to live in.
|
|
65
|
+
#: The package version. The ONLY place it is written: the build reads it from
|
|
66
|
+
#: here (``[tool.setuptools.dynamic]`` in orblet's pyproject), and the packaging
|
|
67
|
+
#: test pins that the installed metadata agrees. A plain literal on purpose —
|
|
68
|
+
#: setuptools parses it from the source without importing the package.
|
|
69
|
+
__version__ = "0.1.0"
|
|
70
|
+
|
|
71
|
+
_LAZY_EXPORTS: dict[str, tuple[str, str]] = {
|
|
72
|
+
# ── Design matrices: one column per linear parameter ───────────────
|
|
73
|
+
"rv_design_matrix": ("orblet.design.columns", "rv_design_matrix"),
|
|
74
|
+
"ti_design_matrix": ("orblet.design.columns", "ti_design_matrix"),
|
|
75
|
+
"acceleration_columns": ("orblet.design.columns", "acceleration_columns"),
|
|
76
|
+
"astrometric_5param_design_matrix": (
|
|
77
|
+
"orblet.solve.astrometry",
|
|
78
|
+
"astrometric_5param_design_matrix",
|
|
79
|
+
),
|
|
80
|
+
|
|
81
|
+
# ── Linear cores: geometry fixed, amplitudes solved exactly ────────
|
|
82
|
+
"linear_solve_rv": ("orblet.solve.rv", "linear_solve_rv"),
|
|
83
|
+
"linear_solve_ti": ("orblet.solve.astrometry", "linear_solve_ti"),
|
|
84
|
+
"best_linear_params_rv": ("orblet.solve.rv", "best_linear_params_rv"),
|
|
85
|
+
"best_linear_params_ti": (
|
|
86
|
+
"orblet.solve.astrometry",
|
|
87
|
+
"best_linear_params_ti",
|
|
88
|
+
),
|
|
89
|
+
"fit_astrometric_5param": (
|
|
90
|
+
"orblet.solve.astrometry",
|
|
91
|
+
"fit_astrometric_5param",
|
|
92
|
+
),
|
|
93
|
+
"recover_K": ("orblet.solve.rv", "recover_K"),
|
|
94
|
+
"recover_omega": ("orblet.solve.rv", "recover_omega"),
|
|
95
|
+
"semi_amplitude_kms": ("orblet.model", "semi_amplitude_kms"),
|
|
96
|
+
|
|
97
|
+
# ── Searching for the period ───────────────────────────────────────
|
|
98
|
+
"scan_ti_frequency": ("orblet.search", "scan_ti_frequency"),
|
|
99
|
+
|
|
100
|
+
# ── Seeds: turning one solution into a starting point for the next ─
|
|
101
|
+
"compose_rv_seed": ("orblet.solve.rv", "compose_rv_seed"),
|
|
102
|
+
"compose_ti_seed": ("orblet.search", "compose_ti_seed"),
|
|
103
|
+
"ti_amplitude_chains": ("orblet.solve.astrometry", "ti_amplitude_chains"),
|
|
104
|
+
"mirror_inclination": ("orblet.elements", "mirror_inclination"),
|
|
105
|
+
|
|
106
|
+
# ── Epochs and time scales ─────────────────────────────────────────
|
|
107
|
+
"prepare_rv_for_orbit": ("orblet.prepare", "prepare_rv_for_orbit"),
|
|
108
|
+
"resolve_epochs_mjd": ("orblet.prepare", "resolve_epochs_mjd"),
|
|
109
|
+
|
|
110
|
+
# ── Where the observer is, as on-sky parallax factors ──────────────
|
|
111
|
+
"per_direction_parallax_factors": (
|
|
112
|
+
"orblet.parallax",
|
|
113
|
+
"per_direction_parallax_factors",
|
|
114
|
+
),
|
|
115
|
+
"OBSERVER_GEOCENTRE": ("orblet.parallax", "OBSERVER_GEOCENTRE"),
|
|
116
|
+
"OBSERVER_L2": ("orblet.parallax", "OBSERVER_L2"),
|
|
117
|
+
"L2_OFFSET_AU": ("orblet.parallax", "L2_OFFSET_AU"),
|
|
118
|
+
|
|
119
|
+
# ── Element conversions and derived curves ─────────────────────────
|
|
120
|
+
"extract_orbital_elements": (
|
|
121
|
+
"orblet.elements",
|
|
122
|
+
"extract_orbital_elements",
|
|
123
|
+
),
|
|
124
|
+
"ti_to_kepler": ("orblet.elements", "ti_to_kepler"),
|
|
125
|
+
"to_nss_convention": ("orblet.elements", "to_nss_convention"),
|
|
126
|
+
"compute_rv_model_curve": ("orblet.elements", "compute_rv_model_curve"),
|
|
127
|
+
"compute_residuals": ("orblet.elements", "compute_residuals"),
|
|
128
|
+
|
|
129
|
+
# ── Carrying one channel's chain into the other's prior ────────────
|
|
130
|
+
"a1_sini_from_rv_chain": ("orblet.rv_chain", "a1_sini_from_rv_chain"),
|
|
131
|
+
"astrometric_priors_from_rv_chain": (
|
|
132
|
+
"orblet.rv_chain",
|
|
133
|
+
"astrometric_priors_from_rv_chain",
|
|
134
|
+
),
|
|
135
|
+
"predicted_k_kms_from_astrometric_chain": (
|
|
136
|
+
"orblet.rv_chain",
|
|
137
|
+
"predicted_k_kms_from_astrometric_chain",
|
|
138
|
+
),
|
|
139
|
+
|
|
140
|
+
# ── Plots (matplotlib arrives here, not before) ────────────────────
|
|
141
|
+
"plot_orbit_fit": ("orblet.plotting", "plot_orbit_fit"),
|
|
142
|
+
"plot_orbit_corner": ("orblet.plotting", "plot_orbit_corner"),
|
|
143
|
+
"plot_astrometric_orbit": ("orblet.plotting", "plot_astrometric_orbit"),
|
|
144
|
+
"plot_astrometric_sky_overlay": (
|
|
145
|
+
"orblet.plotting",
|
|
146
|
+
"plot_astrometric_sky_overlay",
|
|
147
|
+
),
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def __getattr__(name: str):
|
|
152
|
+
"""Resolve a public name on first access (PEP 562).
|
|
153
|
+
|
|
154
|
+
Keeps ``import orblet`` cheap: nothing below the front door is executed
|
|
155
|
+
until something is actually asked for.
|
|
156
|
+
"""
|
|
157
|
+
target = _LAZY_EXPORTS.get(name)
|
|
158
|
+
if target is None:
|
|
159
|
+
raise AttributeError(
|
|
160
|
+
f"module {__name__!r} has no attribute {name!r}"
|
|
161
|
+
)
|
|
162
|
+
module_name, attr = target
|
|
163
|
+
return getattr(importlib.import_module(module_name), attr)
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def __dir__() -> list[str]:
|
|
167
|
+
return sorted(__all__)
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
# Kept as a literal, same names as the table above.
|
|
171
|
+
__all__ = [
|
|
172
|
+
"rv_design_matrix",
|
|
173
|
+
"ti_design_matrix",
|
|
174
|
+
"acceleration_columns",
|
|
175
|
+
"astrometric_5param_design_matrix",
|
|
176
|
+
"linear_solve_rv",
|
|
177
|
+
"linear_solve_ti",
|
|
178
|
+
"best_linear_params_rv",
|
|
179
|
+
"best_linear_params_ti",
|
|
180
|
+
"fit_astrometric_5param",
|
|
181
|
+
"recover_K",
|
|
182
|
+
"recover_omega",
|
|
183
|
+
"semi_amplitude_kms",
|
|
184
|
+
"scan_ti_frequency",
|
|
185
|
+
"compose_rv_seed",
|
|
186
|
+
"compose_ti_seed",
|
|
187
|
+
"ti_amplitude_chains",
|
|
188
|
+
"mirror_inclination",
|
|
189
|
+
"prepare_rv_for_orbit",
|
|
190
|
+
"resolve_epochs_mjd",
|
|
191
|
+
"per_direction_parallax_factors",
|
|
192
|
+
"OBSERVER_GEOCENTRE",
|
|
193
|
+
"OBSERVER_L2",
|
|
194
|
+
"L2_OFFSET_AU",
|
|
195
|
+
"extract_orbital_elements",
|
|
196
|
+
"ti_to_kepler",
|
|
197
|
+
"to_nss_convention",
|
|
198
|
+
"compute_rv_model_curve",
|
|
199
|
+
"compute_residuals",
|
|
200
|
+
"a1_sini_from_rv_chain",
|
|
201
|
+
"astrometric_priors_from_rv_chain",
|
|
202
|
+
"predicted_k_kms_from_astrometric_chain",
|
|
203
|
+
"plot_orbit_fit",
|
|
204
|
+
"plot_orbit_corner",
|
|
205
|
+
"plot_astrometric_orbit",
|
|
206
|
+
"plot_astrometric_sky_overlay",
|
|
207
|
+
]
|