Skip to content

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

  1. 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 with rank_orbits (start_from_positions is superseded by fit_orbit_aliases, which also weighs the aliases), then fit and sample the best orbit with fit.
  2. 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.
  3. 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. Read AliasResult.period_bound and period_at_lower_bound before quoting a period.
  4. Then fit the winner. The best orbit of an alias band starts fit with a KeplerOrbit; 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.