Which orbit tool?
Provisional
May change in any 0.4.x release without a deprecation cycle. virgil.orbit_search is provisional in the whole; virgil.epochs and virgil.aliases too (see stability).
For most data, start with OrbitPipeline: OrbitPipeline.from_oifits(files).run() finds the orbit of a binary seen on several nights without being told the period, and calls the tools below. The orbit tutorial leads with it. The rest of this page is for when you need one of those tools yourself.
virgil has these tools to find a binary's orbit in interferometric data from several epochs. They answer different questions and take the data in different forms. All need the orbits extra (pip install "virgil-astro[orbits]"), which provides jaxoplanet to solve Kepler's equation. All take an Epochs (one named snapshot per dataset), except fit_orbit_aliases, which takes a list of OIData, one per epoch, with per-exposure times.
| You want | Use | Goes in | Comes out | Use it when |
|---|---|---|---|---|
| A quick ranking of trial orbits, or a start for a fit of a model of your own | rank_orbits, or start_from_positions (superseded by fit_orbit_aliases and OrbitPipeline when the period is unknown) to rank and fit in one call |
rank_orbits: Epochs, a function from an orbit to a scene, and the trial orbits (for example from starting_orbits). start_from_positions builds its own trial orbits: it takes Epochs, a scene function of the sampled parameters, priors, start_values(orbit, flux), a position grid, a periods range and t_ref |
RankedOrbits: orbits best first with their scale-marginalized log likelihoods; start_from_positions returns an OrbitStart with the ranked candidates and fits from the best distinct ones |
You have orbits to compare, or a fit that needs a start in the right basin |
| Scores with a flux shared across epochs, with radial-velocity or position terms | score_orbits (provisional) |
a function model(orbit, flux) returning a scene, Epochs, candidate orbits, and optionally RVData or PositionData terms |
OrbitScores: one score per candidate with the companion's flux integrated out, the profiled flux and its error, and flags; rank them with OrbitScores.order |
The candidates are many, the flux is the same on every night, or other measurements of the orbit exist |
| The period aliases of an undersampled orbit, with evidences | fit_orbit_aliases |
A list of OIData, one per epoch, optionally the epoch_positions of the same data, and a period range (by default taken from the data) |
AliasResult: a band for each integer number of orbits between epochs with its evidence and probability, the best orbit, and posterior samples; AliasResult.save and load keep it |
The epochs are further apart than the period, so that more than one period fits the positions |
| One orbit, and its posterior | fit with a KeplerOrbit scene |
epochs.model_fn(scene_fn), where scene_fn maps the sampled parameters to an OrbitalBinary; priors; and epochs.data |
FitResult: the maximum a posteriori orbit; sample the posterior with numpyro_model and NUTS |
You know which period alias is the right one, or the orbit is well sampled |
How to choose
- The sampling constrains the period well. Run
OrbitPipeline, which does the next steps for you (Orbits from interferometric data). By hand: start from per-night positions (epoch_positions), rank trial orbits withrank_orbits(start_from_positionsis superseded byfit_orbit_aliases, which also weighs the aliases), then fit and sample the best orbit withfit. - You have many candidates, a flux shared by all epochs to marginalize over, or radial velocities or astrometry too. Score the candidates with
score_orbits, which adds those terms, then fit the best. - The epochs undersample the period (the gaps are longer than the period). Run
fit_orbit_aliases, which fits each alias band to the observables of all epochs and gives its probability. Flagged nights (an alias peak, a separation below the resolution) seed nothing but still count in the likelihood. ReadAliasResult.period_boundandperiod_at_lower_boundbefore quoting a period. - Then fit the winner. The best orbit of an alias band starts
fitwith aKeplerOrbit; its posterior samples come with the result.
rank_orbits and start_from_positions integrate out each dataset's error scale (scales="marginal", the default), so a dataset with underestimated errors cannot dominate; pass scales="quoted" to rank on the quoted errors. score_orbits marginalizes the same way. Their models differ in shape: rank_orbits takes model(orbit) with the flux fixed, and score_orbits takes model(orbit, flux) and integrates the flux out. Both take the model before the data; score_orbits(epochs, model, ...), the order during 0.4.0 development, still works but warns, and is removed in 0.5.
Angle and sign conventions are on the conventions page, and the helpers behind these tools in orbit methods.