Skip to content

virgil.epochs

Datasets grouped into named epochs, with one snapshot of a moving scene per dataset, for orbit fits to visibilities and closure phases across epochs, and the tools that start and sample those fits: ranking trial orbits by the data, choosing distinct starts for each chain, and starting from a fit of positions.

Named epochs of data, with one snapshot of a moving scene per dataset.

An orbit fit to interferometric data across epochs predicts each epoch's visibilities and closure phases from the scene at that epoch's time. When nothing moves appreciably during an observation (a binary whose period is much longer than a night), one snapshot of the scene per dataset is enough: the orbit is solved once per dataset, and the snapshot is a static model, evaluated on all of the dataset's samples at once on the fast path (e.g. BinaryModelCartesian for OrbitalBinary). That is much cheaper than OIData.model's evaluation of a time-dependent scene at every sample's own time, which remains the choice when the scene moves within an observation.

Epochs keys epochs and datasets by name, so that per-dataset nuisance terms (error scales, wavelength scales, North angles) are attached to the right data however the list is ordered, and it hands fit and numpyro_model what they take: a model function returning one snapshot per dataset, the datasets, and one noise dict per dataset.

Fringe aliases make the likelihood of visibilities multimodal on the scale of the resolution λ/B, so an orbit fit needs a start in the right basin. The tools for starting and sampling such a fit are:

Epochs

Datasets grouped into named epochs, each seen as one snapshot.

Parameters:

Name Type Description Default
epochs dict

{epoch name: data}, where data is one OIData, a list of them, or a dict {dataset name: OIData}. One epoch may hold several datasets, e.g. several files of one night, or two filters a day apart. A lone dataset is named after its epoch; datasets in a list are named "<epoch>[k]". Every name must be unique.

required
at (dataset, epoch)

Where each snapshot is taken: at the mean time of each dataset's samples (default), or at the mean time of the whole epoch, shared by its datasets.

"dataset"
times dict

{epoch or dataset name: MJD}: snapshot times to use instead of the data's own, e.g. for data without per-sample times (AMIGO DISCOs). A dataset's entry overrides its epoch's.

None

Attributes:

Name Type Description
names tuple of str

The epochs, in the order given.

dataset_names tuple of str

The datasets, epoch by epoch.

epoch_of tuple of str

The epoch of each dataset.

data tuple of OIData

The datasets, in the order of dataset_names: pass this as data to fit and numpyro_model.

times ndarray

The snapshot time of each dataset (MJD, float64).

spread_days ndarray

The largest distance in time of each dataset's samples from its snapshot (days). The scene should move by much less than the resolution (λ/B) over this time; if it does not, evaluate the dataset per sample instead (pass it to fit directly with the time-dependent scene).

Examples:

>>> import numpy as onp
>>> from virgil.epochs import Epochs
>>> from virgil.oidata import OIData
>>> def night(mjd):
...     return OIData({"u": [10.0, 20.0], "v": [5.0, -3.0],
...                    "wavel": 2.2e-6, "vis": [1.0, 1.0],
...                    "d_vis": [0.01, 0.01], "mjd": [mjd, mjd + 0.1]})
>>> epochs = Epochs({"2023": {"ut": night(60100.0), "at": night(60101.0)},
...                  "2024": night(60500.0)})
>>> epochs.dataset_names, epochs.epoch_of
(('ut', 'at', '2024'), ('2023', '2023', '2024'))
>>> [round(float(t), 2) for t in epochs.times]
[60100.05, 60101.05, 60500.05]
>>> epochs.noise({"2023": {"vis_scale": 2.0}, "2024": {"vis_scale": 3.0}})
[{'vis_scale': 2.0}, {'vis_scale': 2.0}, {'vis_scale': 3.0}]

names = tuple(epochs) instance-attribute

dataset_names = tuple(names) instance-attribute

epoch_of = tuple(epoch_of) instance-attribute

data = tuple(data) instance-attribute

at = at instance-attribute

resolution_mas property

The finest resolution λ/B_max over the datasets (mas).

Fringe aliases of a companion's position are spaced by about this much, so starting orbits closer together than a fraction of it are one mode.

__init__(epochs, at='dataset', times=None)

__len__()

__repr__()

index(name)

The position of dataset name in data (and in noise).

Fitted per-dataset noise terms are the sites "noise[<index>].<term>" of fit and numpyro_model.

snapshots(scene)

The scene at each dataset's snapshot time, one model per dataset.

Parameters:

Name Type Description Default
scene SourceModel or sequence of SourceModel

One scene for all datasets, or one per dataset (e.g. with per-dataset fluxes). A static scene is its own snapshot.

required

Returns:

Type Description
list of SourceModel

Static models, in the order of data.

model_fn(scene_fn)

A model function for fit/numpyro_model: the snapshots of scene_fn(**values), one per dataset.

noise(terms)

One noise dict per dataset, from terms keyed by name.

Parameters:

Name Type Description Default
terms dict

{epoch or dataset name: {term: prior, value or tied callable}}. An epoch's terms apply to each of its datasets, and each dataset gets its own copy (its own fitted site); a dataset's own terms are added to, and override, its epoch's. To share one fitted value between datasets, tie them to one parameter with callables (see noise_sites).

required

Returns:

Type Description
list of dict

In the order of data, as fit(..., noise=...) takes.

loglike(scene, noise=None)

The log likelihood of all the data, one snapshot per dataset.

The sum over datasets of model_loglike of each snapshot, with noise (values, keyed by name as for :meth:noise) applied to each dataset. Traceable: it can be jitted, differentiated or vmapped in the scene's parameters.

RankedOrbits dataclass

Orbits ranked by the log likelihood of multi-epoch data, best first.

Returned by rank_orbits and chain_starts. Iterating gives (orbit, loglike) pairs, like the (orbit, χ²) pairs of starting_orbits, and ranked[k] is the k-th pair.

Attributes:

Name Type Description
orbits tuple

The orbits, best first.

loglike ndarray

The log likelihood of all the data for each orbit (with the noise values of the ranking), or with scales="marginal" the scale-marginalized score, in the same order.

order ndarray

The position of each orbit in the list that was ranked.

times ndarray

The snapshot times of the datasets (MJD), at which :meth:positions and chain_starts compare orbits.

resolution_mas float

The finest resolution λ/B_max of the data (mas).

orbits instance-attribute

loglike instance-attribute

order instance-attribute

times instance-attribute

resolution_mas instance-attribute

best property

The orbit with the highest log likelihood.

__init__(orbits, loglike, order, times, resolution_mas)

__len__()

__getitem__(k)

__iter__()

positions()

The companion's position (dra, ddec) (mas) of each orbit at each snapshot time, shape (n_orbits, n_times, 2).

EpochPositions dataclass

The companion's position in each dataset, from epoch_positions.

Positions, covariances and gap_marginal come from the scale-marginalized surface (marginal_loglike), on which each observable block's error scale is integrated out: they do not change when a dataset's quoted errors, or only its closure-phase errors, are multiplied by a constant.

Attributes:

Name Type Description
names tuple of str

The datasets.

mjd ndarray

Each dataset's snapshot time (MJD).

dra, ddec ndarray

The fitted positions (mas, East and North).

cov ndarray

The covariance of each position, shape (n, 2, 2) (mas²), from the curvature of the scale-marginalized surface.

flux ndarray

The fitted companion/primary flux of each dataset (at most 1).

gap ndarray

The gap on the quoted errors, as before gap_marginal was added: the log likelihood of the best grid position minus that of the best grid position more than gap_mas from it. It grows as 1/s² when the errors are underestimated by s, so an ambiguous dataset can look decisive. It is a grid quantity, not refined, and its best grid position need not be the fitted one. Kept unchanged for one release, after which gap will hold the marginal value.

gap_marginal ndarray

How decisive each dataset is, on the scale-marginalized surface: the same difference between the best grid position and its best rival more than gap_mas away, with each block's error scale integrated out. A small gap means another peak (a fringe alias, or a mirror image when closure phases are weak) fits almost as well. decisive and min_gap compare this. With refine it compares refined peaks: the best of the catalogue (peaks) and the best of its refined rivals and of the grid points more than gap_mas from it.

chi2_raw tuple of dict

For each dataset, χ²/N on the quoted errors at the fitted position, N = n_independent: per block ("vis", "phi", "extra[k]") and for the whole dataset ("all"). A value well above 1 means the quoted errors are too small; it is not made 1 by the scale marginalization.

scale tuple of dict

For each dataset, the fitted error scale ŝ = √(χ²/ν) of each block ("vis_scale", "phi_scale", "extra[k]_scale").

peaks tuple of EpochPeaks

For each dataset, the catalogue of its peaks, best first: the first is the fitted position.

names instance-attribute

mjd instance-attribute

dra instance-attribute

ddec instance-attribute

cov instance-attribute

flux instance-attribute

gap instance-attribute

gap_marginal instance-attribute

chi2_raw instance-attribute

scale instance-attribute

peaks = () class-attribute instance-attribute

edge property

Whether each dataset's best peak started at the edge of the position grid (see EpochPeaks.edge).

__init__(names, mjd, dra, ddec, cov, flux, gap, gap_marginal, chi2_raw, scale, peaks=())

decisive(min_gap)

Whether each dataset's gap_marginal exceeds min_gap.

positions(*, t_ref, min_gap=None)

The positions as PositionData, for starting_orbits.

Parameters:

Name Type Description Default
t_ref float

The reference time (MJD) of the orbits fitted to them: give the t_ref of your model.

required
min_gap float

Keep only the datasets whose gap_marginal exceeds this. Seeding orbits with an indecisive dataset's position can start every orbit at the wrong peak; the visibility fit judges those datasets instead.

None

EpochPeaks dataclass

The catalogue of one dataset's peaks, from epoch_positions: the highest distinct local maxima of its grid, each refined with refine, best first.

Attributes:

Name Type Description
dra, ddec, flux ndarray

The peaks' positions (mas, East and North) and companion/primary fluxes, after refinement.

loglike ndarray

The log likelihood of each peak on the quoted errors.

marginal ndarray

The scale-marginalized score m of each peak (marginal_loglike); the peaks are sorted by it, highest first.

weight ndarray

The posterior mass of each peak in this dataset alone, summing to 1: the Laplace approximation w ∝ exp(m) det(H)^(-1/2), with H the Hessian of -m at the peak over (dra, ddec, flux), so that a broad peak outweighs a narrow one of the same height. Where laplace is false the peak's height exp(m) stands in.

edge ndarray

Whether each peak started from a grid point on the edge of the position grid. A best peak at the edge may stand for a companion outside the grid: treat that dataset as ambiguous.

laplace ndarray

Whether each peak's weight is its Laplace mass: false without refine, or where the Hessian is not positive definite (a peak on a bound, or a flat direction).

dra instance-attribute

ddec instance-attribute

flux instance-attribute

loglike instance-attribute

marginal instance-attribute

weight instance-attribute

edge instance-attribute

laplace instance-attribute

__init__(dra, ddec, flux, loglike, marginal, weight, edge, laplace)

__len__()

OrbitStart dataclass

A start for a multi-epoch orbit fit, from start_from_positions.

Attributes:

Name Type Description
positions EpochPositions

The per-dataset positions that seeded the orbits.

candidates RankedOrbits

The starting orbits, ranked by the visibilities.

fits tuple of FitResult

The fits to the visibilities from the distinct best candidates, lowest loss first, and those with a non-finite loss last. Each info holds the candidate it started from (its rank in candidates); a fit that did not converge, or stopped at its time_limit, says so in info["converged"] and info["stop"].

data Epochs

The data.

failed tuple of (int, str)

The candidates whose refinement fit raised an error, with the error.

seeded tuple of str

The datasets whose positions seeded the starting orbits (a gap_marginal above min_gap).

ambiguous tuple of str

The other datasets: their best peak has a rival within min_gap, so their positions did not seed the orbits. They still enter the ranking and the fits through their visibilities; their peaks are in positions.peaks.

positions instance-attribute

candidates instance-attribute

fits instance-attribute

data instance-attribute

failed = () class-attribute instance-attribute

seeded = () class-attribute instance-attribute

ambiguous = () class-attribute instance-attribute

best property

The fit with the lowest loss.

__init__(positions, candidates, fits, data, failed=(), seeded=(), ambiguous=())

modes(*, max_delta_loss=10.0, same_mode_chi2=1.0)

The distinct fits, lowest loss first.

Two fits are one mode when their predictions differ by less than same_mode_chi2 in χ² (the sum of squared differences of their whitened residuals, with the quoted errors). Fits whose loss is more than max_delta_loss above the best's are dropped: a difference Δ in loss is a factor of about e^Δ in posterior density. Fits with a non-finite loss are left out.

chain_values(n_chains, **options)

One start (fitted values) per chain, cycling through the distinct modes (see :meth:modes, which takes options).

Pass them to chain_init_params to start each chain of numpyro's MCMC in its own mode.

rank_orbits(model, data, orbits, *, scales=None, noise=None, dof=1.0, s_max=None, batch_size=None, dtype='float64')

Rank trial orbits by the log likelihood of multi-epoch data.

Each orbit's scene is evaluated at every dataset's snapshot time and compared with all the data at once, by one compiled kernel mapped over the orbits. This judges trial orbits, e.g. from a Thiele–Innes grid on rough positions (starting_orbits) or from prior draws, by the interferometric data themselves rather than by the positions that seeded them.

Parameters:

Name Type Description Default
model callable

Maps an orbit to a time-dependent scene, e.g. lambda orbit: OrbitalBinary(orbit, 0.1). It is traced, so it must not convert the orbit's elements to Python floats; other parameters (fluxes, the shape of an extended component) are fixed at trial values.

required
data Epochs

The data, one snapshot per dataset.

required
orbits sequence or orbit

A list of orbits, the (orbit, χ²) pairs of starting_orbits, a RankedOrbits, or one orbit whose elements have a leading axis (e.g. prior draws). All must share one class and t_ref.

required
scales (quoted, marginal)

How the errors are scaled. "quoted" ranks by Epochs.loglike on the quoted errors (with noise), so the datasets with the most underestimated errors dominate the ranking. "marginal" integrates each dataset's error scale out, block by block (visibilities, phases), under its Jeffreys prior, and ranks by the sum of marginal_loglike: the ranking then does not change when one dataset's errors are rescaled. Data with gains, closure-phase offsets or a model-dependent covariance are refused (see marginal_loglike). The default is "quoted" for now, with a FutureWarning when scales is not given; it will become "marginal".

"quoted"
noise dict

Noise values keyed by epoch or dataset name, as for Epochs.loglike; by default the quoted errors. With scales="marginal", error scales are integrated out and only the other terms (e.g. wavel_scale) matter.

None
dof optional

For scales="marginal": the effective-dof fraction (one number, or a dict keyed by dataset or epoch name) and the bound on the scales, as for marginal_loglike.

1.0
s_max optional

For scales="marginal": the effective-dof fraction (one number, or a dict keyed by dataset or epoch name) and the bound on the scales, as for marginal_loglike.

1.0
batch_size int

Orbits evaluated together by jax.lax.map (default: all of them at once). Lower it if the data are large.

None
dtype (float64, float32)

Precision of the evaluation (default float64, in a local jax.enable_x64 context).

"float64"

Returns:

Type Description
RankedOrbits

The orbits sorted by log likelihood, best first. An orbit whose likelihood is not finite ranks last, with -inf.

Examples:

>>> candidates = starting_orbits(positions, periods)
>>> ranked = rank_orbits(lambda o: OrbitalBinary(o, 0.1), epochs,
...                      candidates, scales="marginal")
>>> ranked.best, ranked.loglike[:3]

chain_starts(ranked, n_chains, *, min_distance_mas=None)

The best distinct orbits of a ranking, one per chain.

Starting every chain at one best fit hides other modes; starting chains in different good modes (the best few distinct orbits) lets the chains show whether the posterior is multimodal. Two orbits are the same mode when the companion's positions at every snapshot time agree to within min_distance_mas: the mirror orbit (Ω + 180°, ω + 180°), which has the same sky motion, is the same mode, while a fringe alias or a period alias is not.

Parameters:

Name Type Description Default
ranked RankedOrbits

From rank_orbits.

required
n_chains int

Number of starts.

required
min_distance_mas float

The largest difference in position (mas) at any snapshot time for two orbits to count as one mode; by default half the finest resolution λ/B_max of the data.

None

Returns:

Type Description
RankedOrbits

n_chains orbits, best first. If there are fewer distinct orbits than chains, the distinct orbits are repeated in turn. Map each orbit to your model's parameters (e.g. period, dt_peri) to start a chain there.

epoch_positions(data, grid, *, gap_mas=None, refine=True, n_peaks=5, dof=1.0, s_max=None, batch_size=4096)

The companion's position in each dataset of a binary.

For each dataset: a static binary (BinaryModelCartesian) on a grid of positions and fluxes, scored on the scale-marginalized surface m = -Σ_b (ν_b/2) ln χ²_b (marginal_loglike), in which the error scale of each observable block (visibilities, closure phases) is integrated out under its Jeffreys prior. The n_peaks highest distinct local maxima of the grid (more than gap_mas apart) are each refined (with refine) by maximizing m, which is the fit with a free vis_scale and phi_scale per dataset, profiled, and ranked by their refined m: the best grid point is not always the best peak once refined, since fringe peaks are often narrower than the grid step. The best refined peak is the position, and the curvature of m there gives its covariance. Every peak is kept in the catalogue EpochPositions.peaks. The positions, covariances and gap_marginal therefore do not depend on how well each block's errors were quoted, and nights whose errors are underestimated do not look more decisive than they are. These positions are only a starting point for a fit of the orbit to the visibilities: when the scene is more than two point stars they can be biased.

The raw χ²/N on the quoted errors and the fitted scale of each block are recorded, and a UserWarning names the datasets whose raw χ²/N exceeds 4 (errors underestimated by more than 2): their orbit fits need fitted error scales, and a rescaled χ²/N ≈ 1 does not make them good fits.

Parameters:

Name Type Description Default
data Epochs

The data. Datasets with gains, closure-phase offsets or a model-dependent covariance are refused (see marginal_loglike).

required
grid dict

{"dra": axis, "ddec": axis, "flux": values}: the positions (mas) and companion/primary fluxes to try. The position step should be finer than the resolution λ/B of the longest baseline. Fluxes must be in (0, 1]: a binary with flux ratio f > 1 at r is the one with 1/f at -r, so f > 1 would only add a twin of every peak.

required
gap_mas float

The distance (mas) beyond which another peak counts as a rival for gap and gap_marginal; by default each dataset's resolution λ/B_max.

None
refine bool

Refine each grid peak by maximizing m (default). Otherwise the position is the best grid point, with a covariance of one grid step squared, and the peaks are the grid points.

True
n_peaks int

The peaks catalogued and refined per dataset (default 5). Each refinement is a small fit, so the cost of refine grows linearly with n_peaks; 1 refines only the best grid point, which can commit to the wrong peak.

5
dof float or dict

The effective-dof fraction ν_eff/ν of each dataset's blocks (one number, or a dict keyed by dataset or epoch name; default 1), for errors correlated beyond the quoted ones (see marginal_loglike). It multiplies gap_marginal by dof and cov by 1/dof.

1.0
s_max float

Bound the scales and integrate them out numerically (see marginal_loglike), for weak closure phases with a large scale.

None
batch_size int

Grid points evaluated together.

4096

Returns:

Type Description
EpochPositions

marginal_loglike(model, data, *, dof=1.0, s_max=None, **noise)

The log likelihood with each block's error scale marginalized.

Each observable block b of data (visibilities, phases, and each extra observable) has an unknown factor s_b on its quoted errors. Integrating it out under its Jeffreys prior 1/s_b gives, up to a constant,

m = -Σ_b (ν_b/2) ln χ²_b,

where χ²_b is the block's χ² on the quoted errors (from whitened_residuals) and ν_b its number of independent observables (n_independent, split by block). Profiling s_b gives the same function, at ŝ_b² = χ²_b/ν_b. m does not change when any block's errors are multiplied by a constant, so its differences between models (e.g. the gap between two peaks) do not depend on how well the errors were quoted. Near a peak, m ≈ -χ²/(2ŝ²): differences are those of the quoted-error log likelihood divided by ŝ².

Every block uses the Gaussian (small-σ) normalization, including uncorrelated closure phases, whose likelihood in model_loglike is a von Mises density; for those the unbounded marginal would be improper as s → ∞. m is a search surface, not a replacement for model_loglike. Where sσ is not small (weak closure phases with a large scale), pass s_max.

Data whose likelihood has a model-dependent normalization are refused with a NotImplementedError: gains (OIData.with_gains), closure-phase offsets (OIData.with_closure_offsets) and extra observables with a model-dependent covariance. Their marginalized nuisance covariance is not multiplied by an error scale, so a block's χ² is not ∝ 1/s² and the scale would not be integrated out. The same holds for epoch_positions and rank_orbits(scales="marginal").

Parameters:

Name Type Description Default
model SourceModel

Model to evaluate.

required
data OIData

Data to compare with.

required
dof float

The effective number of degrees of freedom as a fraction of ν_b (default 1). Errors correlated beyond the model of the quoted errors carry fewer than ν_b degrees of freedom, and m with ν_b is then over-confident by about ν_b/ν_eff; a dof below 1 (e.g. calibrated by a residual bootstrap) scales every difference of m down by that factor.

1.0
s_max float

Bound each scale to [1/s_max, s_max] and integrate it out numerically, with the exact von Mises normalization for uncorrelated closure phases: m is then Σ_b ln ∫ L_b(s)^dof d ln s over the bounds. The integral is adaptive in ln s (scans narrow it to where the integrand matters, then Gauss–Legendre), so it stays accurate where the likelihood falls steeply from a bound. This is finite and proper where the unbounded marginal is not.

None
**noise

Other noise terms (e.g. wavel_scale), as for whitened_residuals. vis_scale and phi_scale should be left out: the scales are integrated out.

{}

Returns:

Type Description
float

start_from_positions(model, priors, data, start_values, *, grid, periods, t_ref, scales=None, noise=None, dof=1.0, s_max=None, eccs=None, n_phase=36, n_candidates=200, n_refine=4, min_gap=5.0, refine_positions=True, n_peaks=5, batch_size=None, **fit_options)

Start an orbit fit to multi-epoch visibilities from a positions fit.

The likelihood of visibilities is multimodal on the scale of the resolution λ/B (fringe aliases), and a sampler started from default values can stick in an alias. This builds a start in four steps:

  1. Positions: the companion's position in each dataset, the best of its refined grid peaks (epoch_positions).
  2. Starting orbits: a Thiele–Innes grid over period, eccentricity and time of periastron on the decisive datasets' positions (starting_orbits). An ambiguous dataset does not seed them; its other peaks are kept in positions.peaks, but only its visibilities judge between them here.
  3. Ranking of those orbits by the likelihood of all the visibilities, with each orbit mapped to the model's parameters by start_values and the flux at the median of the positions fits.
  4. Refinement with fit of the full model (noise terms included) from the n_refine best distinct orbits (as for chain_starts).

Only the start comes from positions: nothing here enters the posterior. Sample from the best fit with numpyro's init_to_value, or start one chain in each mode with OrbitStart.chain_values and chain_init_params.

Parameters:

Name Type Description Default
model callable

The scene function, called with the parameters in priors as keyword arguments and returning a time-dependent scene, as for Epochs.model_fn.

required
priors dict

The priors of the fit, as for fit.

required
data Epochs

The data.

required
start_values callable

start_values(orbit, flux): the starting values (a dict with a value for every key of priors) for a KeplerOrbit (with t_ref) and a companion/primary flux. It may use Python floats.

required
grid dict

The per-dataset grid of positions and fluxes (see epoch_positions).

required
periods array - like

Trial periods (days) for starting_orbits. Build them with period_grid over the times of the datasets: a UserWarning says when neighbouring periods would drift apart by more than 1/9 of a cycle over the baseline of the datasets that seed the orbits, so that the grid can miss the true period.

required
t_ref float

The reference time (MJD) of the starting orbits: the t_ref of your model.

required
scales (quoted, marginal)

How step 3 ranks the candidates (see rank_orbits): "quoted" on the quoted errors, where the datasets with the most underestimated errors dominate, or "marginal" with each dataset's per-block error scale integrated out, so that each dataset counts by its own fitted scale ŝ. The default is "quoted" for now, with a FutureWarning when scales is not given; it will become "marginal".

"quoted"
noise dict or list of dict

Noise terms of the refinement fits, as for fit (e.g. from Epochs.noise). The ranking does not use them.

None
dof optional

Passed to epoch_positions and, with scales="marginal", to the ranking.

1.0
s_max optional

Passed to epoch_positions and, with scales="marginal", to the ranking.

1.0
eccs optional

Passed to starting_orbits.

None
n_phase optional

Passed to starting_orbits.

None
n_candidates int

Starting orbits to rank (default 200).

200
n_refine int

Distinct orbits to refine (default 4).

4
min_gap float

Seed orbits only with datasets whose positions are decisive: a gap_marginal above this (see EpochPositions). This compares the scale-marginalized gap, so a night whose errors are underestimated no longer passes it with a gap inflated by s². The datasets left out are named in OrbitStart.ambiguous, with a UserWarning.

5.0
refine_positions bool

Refine each grid peak with a fit (see epoch_positions).

True
n_peaks int

Peaks refined per dataset (see epoch_positions); the cost of step 1 grows linearly with it.

5
batch_size int

Orbits ranked together (see rank_orbits).

None
**fit_options

Passed to the refinement fits (e.g. method, max_steps, time_limit in seconds per fit, progress). A fit that raises an error is recorded in OrbitStart.failed, with a RuntimeWarning, and the others go on; one that stops at its time_limit is kept, unconverged. A TypeError or ValueError (such as a misspelt option) does not depend on the start, and is raised at once.

{}

Returns:

Type Description
OrbitStart

Raises:

Type Description
RuntimeError

If every refinement fit raised an error (chained to the last).