Add further orbit dissimilarity criteria, orbit classification and threshold methods - #97
Conversation
Adds the criteria that the literature offers besides D_SH, D_D, D_H and D_N, and
a new module for the criteria that classify a single orbit rather than comparing
two. The existing functions are untouched: this commit is 443 insertions and 0
deletions to Dcriteria.py.
New in Dcriteria.py:
calcRho1, calcRho2, calcRho5 Kholshevnikov, Kokhirova, Babadzhanov & Khamroev
(2016), MNRAS 462, 2275
calcC Neslusan (2002)
calcDR Valsecchi, Jopek & Froeschle (1999), MNRAS 304, 743
calcDB, calcDT Jenniskens (2008), Icarus 194, 13
calcDX Rudawska, Matlovic, Toth & Kornos (2015), P&SS 118, 38
calcDVJopek Jopek, Rudawska & Bartczak (2008), EM&P 102, 73
New in OrbitClassification.py: calcTisserand (Whipple 1954), calcKresakK,
calcKresakP, calcAphelionDistance, and the two-parameter criteria isCometaryQi,
isCometaryKi, isCometaryPi (Jopek & Williams 2013, MNRAS 430, 2377, eqs 8-12).
The rho metrics are the only ones here that satisfy the triangle inequality, and
the only ones defined for circular orbits. Both properties are tested, with D_D
as the control: the triple
(0.955, 0.348, 168.20 deg, 184.34 deg, 29.66 deg)
(0.959, 0.441, 168.33 deg, 186.31 deg, 153.67 deg)
(0.965, 0.900, 168.27 deg, 184.55 deg, 209.86 deg)
breaks the D_D triangle inequality by 0.0679, while rho_2 has 0.2298 of slack on
the same triple. D_D also raises ZeroDivisionError for two circular orbits, which
rho_1, rho_2 and rho_5 handle.
All new functions are written against np rather than math and accept scalars or
arrays, which also removes the branch class that produced the D_H node sign bug.
Thresholds are stated per criterion in the docstrings. None of these seven papers
publishes one, so no default threshold is supplied. They would not be transferable
anyway: rho carries units of sqrt(AU) with L = 1 AU, while D_SH and D_D are
dimensionless.
Verification. rho_1, rho_2 and rho_5 reproduce table 2 of Kholshevnikov et al.
(2016) for 96P/Machholz 1 and 2003 EH1 at 7415 BC:
computed paper
rho_1 0.0346 0.036
rho_2 0.0357 0.035
rho_5 0.0154 0.016
The residual is set by the precision of the published elements: perturbing them
within the half-width of their quoted rounding moves rho_2 over [0.0354, 0.0360].
The test tolerance is 0.002, and every plausible transcription error of the
formulae was measured to miss by at least 0.0099, five times that: flipping the
sign of Delta in cos P gives 0.0450, dropping the e1*e2 term gives 1.1875, using
cos P in place of cos I gives 0.0475.
The paper's closed form for cos P was checked independently against the dot
product of the two perihelion unit vectors, agreeing to 5.4e-16 over 20000 random
pairs. calcTisserand gives 3.026 for 2P/Encke against the literature 3.03, and
exactly 3 for an orbit identical to Jupiter's. Where no paper value exists the
tests use analytic identities: rho_5 <= rho_2 with rho_5 attaining the minimum of
rho_2 under a scan over both nodes and both arguments of perihelion, D_R <= D_N
against the existing calcDN, C against the angular momentum term of rho_1, and
the D_V energy term against -1/(2a).
A self-comparison cannot return exactly zero for the same reason as in the D_SH
family: the square of the metric cancels to the 1e-16 rounding level and the
square root lifts that to about 1e-8. Measured over 5000 random orbits the floor
is 3.7e-08 for rho_1, 6.0e-08 for rho_2 and exactly 0 for rho_5, so the tests
allow 1e-7.
Two criteria are shipped with required rather than default weights. Rudawska et
al. (2015) leave the four D_X weights undefined and give no method for choosing
them, and do not state the units of V_g, which matter because the velocity
difference enters the radiant terms as |V_g1 - V_g2| + 1. Jopek et al. (2008)
define the D_V weights as dispersions measured over a reference set of showers,
so no universally valid default exists; the paper itself is paywalled and its
tabulated weight values could not be read, which is why they are not supplied
here. The 1.5 and 2 coefficients in D_V follow the only accessible transcription
of its equation and have not been checked against the original.
calcDV, the unpublished Vida criterion already in this module, is deliberately
left under its existing name so the public API does not break; the new function
is calcDVJopek.
Asher, Clube & Steel (1993) is not included. The paper is paywalled and the ADS
scan's OCR loses the normalising constant of the semi-major axis term. The
structure, the reference orbit (a = 2.1 AU, e = 0.82, i = 4 deg) and the threshold
(D = 0.15) are legible, but fitting the recoverable form to the paper's own table
1 leaves an rms of 0.031 against D values quoted to two decimals, five times the
rounding floor, so the equation has not been reconstructed correctly and is not
guessed at here.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A D criterion is only half of a stream search; the threshold below which two
orbits count as associated has to be chosen too, and a value taken from the
literature is tied to the criterion, the database size and the sporadic
background of the study it came from.
New module Dthresholds.py:
thresholdDr D_r = 0.20 (360/N)^(1/4), Southworth & Hawkins (1963);
Lindblad (1971), Smithson. Contrib. Astrophys. 12, 1
thresholdBreakPoint Neslusan, Svoren & Porubcan (1995), EM&P 68, 427;
Neslusan, Hajdukova & Jakubik (2013), A&A 560, A47
thresholdReliability Jopek, Valsecchi & Froeschle (1999), MNRAS 304, 751
thresholdDr returns 0.20 at its anchor of 360 orbits and scales as the fourth
root, so sixteen times the orbits halves it; both are tested exactly.
thresholdReliability builds synthetic samples by shuffling each parameter
independently, which preserves every marginal distribution and destroys the
correlations a real stream would carry, then returns the largest threshold at
which the requested fraction of those samples contains no associated pair. The
test re-draws shuffled samples with an independent seed and confirms the returned
threshold really does leave them free of pairs at the promised rate. Only pairs
are considered, since anything larger needs a clustering algorithm, which is a
separate concern from the threshold.
thresholdBreakPoint locates the break as the bin of strongest negative curvature
of the cumulative distribution. On a synthetic sample of a stream uniform over
[0, 0.05] plus a background uniform over [0.30, 1.0] it returns 0.04997, i.e. the
stream edge to within one bin, which is the tightest threshold that still takes
the whole stream. Note the caveat the method carries: the break is only meaningful
when the stream is a large enough fraction of the sample to bend the curve, and
the function will still return its best candidate when it is not.
Jopek & Bronikowska (2017), P&SS 143, 43 is not included. Its threshold depends on
the D function as well as on the sample size, but the paper is paywalled and no
accessible source states the dependence, so it is not guessed at here. The
break-point method is implemented as described in the accessible literature; the
1995 paper's ADS scan carries no text layer, so its exact formalisation of where
the turn is located could not be read.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
calcRho1 cited Kholshevnikov et al. as (1993); the paper is (2016), as the other two rho docstrings already had it. The Kresak references carried volume and page numbers that could not be checked against the original 1967 and 1969 papers, so they are reduced to author and year. The definitions themselves are unaffected: they are taken from Jopek & Williams (2013), eqs 11 and 12, which is cited on the following line in each docstring and was verified directly. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Keeps every new line inside the 110 column width the module already uses. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Fills the omission noted when Dthresholds.py was added: Jopek & Bronikowska
(2017), P&SS 143, 43, doi:10.1016/j.pss.2016.12.004, whose threshold depends on
the D function and on the population as well as on the sample size.
New: thresholdRandomPairing(n_orbits, d_criterion, population), returning the
threshold at which the probability of a coincidental pair is 0.01. The twelve
published coefficient pairs are exposed as RANDOM_PAIRING_COEFFS:
D_c = A*N**(-b)
criterion population A b
D_SH bolides 0.3186 0.431 (eq. 14)
D_H bolides 0.3143 0.438 (eq. 15)
D_D bolides 0.1240 0.423 (eq. 16)
D_SH NEAs 0.2558 0.408 (eq. 17)
D_H NEAs 0.2193 0.405 (eq. 20)
D_D NEAs 0.1049 0.408 (eq. 23)
D_SH radar 0.4007 0.450 (eq. 18)
D_H radar 0.3768 0.447 (eq. 21)
D_D radar 0.1543 0.436 (eq. 24)
D_SH video 0.5837 0.487 (eq. 19)
D_H video 0.4808 0.464 (eq. 22)
D_D video 0.1724 0.446 (eq. 25)
An unpublished combination raises ValueError rather than falling back on a
default, since no threshold exists to fall back to.
Verification against the paper's own tables:
- eq. 14 reproduces its tabulation in column D_(14) of table 3 to 7.7e-05.
- the nine large-sample formulae track the measured thresholds of table 7 to
6.4e-04 absolute and 5.0% relative over 200 to 16000 orbits. They are
least-squares fits through those points, so they are not expected to pass
through each one exactly; the tests allow 1e-03 and 6%.
- the criterion ratios of table 8 follow from the formulae alone: D_SH/D_H and
D_SH/D_D come out as 1.14/2.44 for NEAs, 1.03/2.30 for radar and 0.99/2.38
for video, matching all six published averages to two decimals.
This also supplies a real paper value for thresholdDr, which until now was tested
only against its own anchor. Formula 1 reproduces column D_(1) of table 3 to
4.2e-05, inside the 5e-05 rounding floor of a four-decimal column.
The paper's practical conclusion is recorded in thresholdDr's docstring: for
bolide samples of 200 to 800 orbits the scaled Southworth & Hawkins threshold
corresponds to a coincidental pairing probability of 1.0, so a "similar" pair is
certain whether or not any orbits are related. It runs 7 to 9 times looser than
the threshold that actually delivers 0.01, which is asserted in the tests.
Also noted: the thresholds depend strongly on how the synthetic orbits are drawn.
Drawing the elements uniformly rather than reproducing the observed distributions
including the Earth-crossing condition inflates the threshold by about a factor
of two, which is why thresholdReliability shuffles the observed values instead of
sampling a box.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Fills the omission noted when the criteria were added. calcDACS implements eq. 2
of Asher, Clube & Steel (1993), MNRAS 264, 93, doi:10.1093/mnras/264.1.93:
D_ACS^2 = ((a1 - a2)/3)^2 + (e1 - e2)^2 + (2 sin((i1 - i2)/2))^2
The Taurid Complex core orbit it is normally evaluated against is exposed as
TC_REFERENCE_A = 2.1 AU, TC_REFERENCE_E = 0.82 and TC_REFERENCE_INCL = 4 deg, and
the scale of the semi-major axis term as DACS_A_SCALE = 3 AU.
The criterion deliberately carries no node or longitude term. The Taurid Complex
has been dispersed in longitude of perihelion by Jovian perturbations, so a
longitude term appropriate to a narrow stream would dominate the sum; the paper
tests longitude alignment separately, after selecting on (a, e, i). That is
recorded in the docstring, since it is the reason the signature is as narrow as
it is.
Published thresholds, both in the docstring: D = 0.15 restricts the selection to
the core of the complex, as used with the perihelion-distance form in Steel, Asher
& Clube (1991); the 1993 paper suggests D of about 0.2 as the value that best
defines Taurid Complex asteroids.
Determination of the scale. Table 1 of the paper cannot be recomputed from its own
columns, because the inclination is first adjusted by Brouwer (1947) secular
perturbation theory to the smallest value the orbit ever reaches, and the
eccentricity likewise, while the table reports observed values. The semi-major
axis is not adjusted, and the other two terms are non-negative, so the semi-major
axis term is a rigorous lower bound on every tabulated D. That bounds the scale
from below. It is also attained exactly, by P/Encke and by 1991 TB2, whose
adjusted eccentricity and inclination therefore coincide with the reference orbit,
which bounds it from above. The two together fix the scale at 3 AU from the table
alone:
scale outcome
1.0 lower bound violated by 0.3100
2.0 lower bound violated by 0.0500
2.5 lower bound violated by 0.0200
3.0 bound holds on all 26 rows and is attained by 2 of them
3.5 bound slack everywhere, largest ratio 0.857
4.0 bound slack everywhere, largest ratio 0.750
Both tests were run against substituted scales to confirm they reject every value
but 3.0, so they pin the published constant rather than merely accepting it.
This also explains why the constant was previously left unimplemented. Fitting the
recoverable form to table 1 gave a best scale of 3.87 with an rms of 0.031, five
times the rounding floor, and residuals correlating with inclination. The
correlation was the secular adjustment: the fit was reconciling a D computed from
the minimum inclination against the observed inclination in the table. The
equation was recoverable all along; the table was not the right thing to fit to.
Because of that, the docstring states plainly that no adjustment is applied here
and that passing observed elements will not reproduce the paper's table. It also
notes the perihelion-distance form of Steel, Asher & Clube (1991), MNRAS 251, 632,
which drops the scale factor and suits meteoroids, whose q is better determined
than their a, where the form implemented here suits asteroids.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
calcDACS was added in the semi-major axis form alone, which made it the only
orbital-element criterion in this module that cannot be evaluated from the
elements the rest of the module works in. Nothing justified leaving the earlier
form out: it needs strictly less than the others.
calcDSAC implements eq. 1 of Asher, Clube & Steel (1993), attributed there to
Steel, Asher & Clube (1991), MNRAS 251, 632:
D^2 = (q1 - q2)^2 + (e1 - e2)^2 + (2 sin((i1 - i2)/2))^2
The reference perihelion distance of the Taurid Complex core is added as
TC_REFERENCE_Q = 0.375 AU, alongside the existing TC_REFERENCE_E and
TC_REFERENCE_INCL, which both forms share.
The two forms are not interchangeable and the docstrings now point at each other.
The perihelion form is for meteoroids, whose q is better determined than their a,
since a carries the full weight of the velocity uncertainty. The semi-major axis
form is for asteroids, whose a is well determined. The perihelion term carries no
scale factor, where the semi-major axis term is divided by 3 AU, so a 1 AU
difference contributes 1 to the first and 1/3 to the second.
A second difference worth recording: the semi-major axis form has to have its
inclination secularly adjusted before use, while this form does not when applied
to meteoroids, because an orbit must cross the Earth's to produce a meteor, which
already constrains the inclination to be low. That is in the docstring.
On the data each criterion needs. After this commit every orbital-element
criterion in the module is evaluable from (q, e, i, node, peri), which is what the
command line accepts:
q, e, i, node, peri calcDSH, calcDD, calcDH, calcRho1, calcRho2, calcRho5,
calcC, calcDVJopek, calcDB (no q), calcDSAC (no node,
peri)
a, e, i calcDACS, calcDT, via a = q/(1 - e)
radiant and speed calcDN, calcDR, calcDV, calcDVuncert, calcDX
Only the last group needs anything beyond the orbital elements, which is inherent
to criteria defined on geocentric quantities and predates this work.
Verification. No paper value exists for this form: the 1993 paper's table 1 is
computed with eq. 2, and the 1991 paper was not accessible, so the tests are
identities. The sharpest is that the two forms must describe the same reference
orbit, and they do: a1(1 - e1) = 2.1 x 0.18 = 0.3780 AU against the published
q1 = 0.375 AU, which sits inside [0.3588, 0.3978] AU, the range implied by the two
significant figures of a1 and the two decimals of e1. The eccentricity and
inclination terms are shared between the two forms, so stripping the leading term
from each must leave the same remainder, checked to 1e-9 over 300 orbit pairs.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The primary papers for these three became available, and they contradict the
provenance caveats recorded when they were added. All three formulae were correct
as implemented; the documentation around them was not.
D_X: the weights are published, contrary to the caveat
Rudawska et al. (2015) state the weights outright, in section 2.2: "To normalise
contributions of each term in D_X, we used values: w_lambda = 0.17, w_alpha =
1.20, w_delta = 1.20, and w_v = 0.20." They are now the defaults, exposed as
DX_W_SOL, DX_W_RA, DX_W_DEC and DX_W_VG and still overridable, rather than
required arguments.
The other two caveats also fall. The velocity unit is not ambiguous: the paper
tabulates Vg in km/s throughout, and that is now stated. A threshold is published:
groups merge when D_X <= 0.15, from their step 2.
Verified end to end against values the paper quotes for the mean D_X within three
showers. Drawing members from each shower's mean and its tabulated dispersions in
table 2:
computed paper
GEM 0.061 0.06
PER 0.101 0.09
ORI 0.070 0.07
The test allows 0.02. It rejects unit weights, which give 0.073, 0.116 and 0.083,
but it does not pin the four weights individually: weight sets of similar
magnitude also pass. That limitation is stated in the test docstring, and the
weight values are additionally asserted against the published numbers directly.
A second check uses real table 2 rows: the paper reports that its method keeps the
Southern and Northern Taurids separate, and their tabulated means give D_X = 0.244
and 0.241 depending on order, above the 0.15 merge threshold.
The asymmetry is now documented rather than left to be discovered. The right
ascension term is scaled by cos of the first declination and the velocity term by
the first velocity, so exchanging the orbits changes the result, by 0.003 in the
Taurid case above. The paper applies the criterion to a group mean against a group
mean, where this does not matter.
One measured finding that does not support the paper's stated rationale: the
weights are described as normalising each term's contribution, but over pairs
drawn within a shower from the dispersions of table 2 the root-mean-square term
contributions span a factor of 44 with the published weights against a factor of
7.4 with unit weights, the right ascension term dominating. This is not asserted
anywhere in the code; the docstring records the weights and their source without
repeating the claim.
D_T: thresholds exist, and the paper writes it in q and e
Jenniskens (2008) publishes thresholds for both of his criteria, in section 3.2:
D_T < 0.3 with D_B < 1.0 identifies parent bodies and siblings, and D_T < 0.6 with
D_B < 1.5 also captures bodies related through an earlier fragmentation. The
docstrings previously said no threshold was published for either.
His eq. 13 writes the Tisserand parameter in terms of q and e rather than a,
"because the observational errors in these orbital elements are smaller than those
in a". calcDT now takes q rather than a, which follows the paper and also brings it
into the variables the rest of the module works in. The two forms agree to 2.1e-14
over 200000 random orbits, and the q form has a concrete advantage: at e = 1 the
semi-major axis diverges and the (a, e, i) form evaluates to nan, while eq. 13
stays finite. Both properties are tested.
T_J now has paper values behind it. Against table 1: 2P/Encke computes 3.0264
against 3.03, and 3200 Phaethon 4.5108 against 4.51.
D_B: better provenance and the published thresholds
The formula is confirmed exactly, including the 0.13, 0.06 and 14.2 deg
dispersions. The docstring now records where the invariants come from rather than
describing them vaguely: C1 from the z-component of the angular momentum and the
energy, C2 as the Lidov (1961, 1962) integral of the twice-averaged three-body
problem, C3 as the longitude of perihelion, all three via Babadzhanov (1989). It
also records the caveat the author states and does not correct for, that C1, C2
and C3 are not orthogonal.
Note on section 2.1 of Jenniskens (2008), which restates the older criteria and
should not be used as a source for them. Its eq. 9 writes the last D_H term with
(e1 - e2)/2 where (e1 + e2)/2 belongs, the same error as in the recent review; its
eq. 5 writes the D_D eccentricity term with (e1 - e2) in the denominator; and its
eq. 3 writes cos((i1 - i2)/2) in Pi_21 where cos((i1 + i2)/2) belongs. That last
one was checked against the mutual-node vector definition over 200000 pairs: the
form in this codebase agrees 100.00% of the time and the form as printed agrees
0.00% of the time, so no change is warranted. The author's own criteria, D_B and
D_T, are correct.
Its eq. 3 does independently confirm the node sign convention of the companion
bug-fix PR, stating that "the sign of arcsin should be opposite when
|Omega_1 - Omega_2| > 180 deg".
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A fresh read of the three new modules turned up one wrong statement, two claims
made about papers that were never read, an argument order that invited silent
misuse, a test that would misreport half its possible inputs, and a handful of
inconsistencies. No formula changed; one criterion was added.
Wrong statement
calcRho1 claimed the angular momentum term "carries units of length while the
eccentricity term is dimensionless". Both terms are dimensionless: the first is a
length divided by L. The real point, now stated, is that the choice of L fixes how
the two are weighted against each other and so fixes the numerical scale.
Absence claims about papers that were not read
calcC and calcDVJopek both asserted "No threshold is published". Neither source
was accessible, so that was an unverifiable claim about an absence, which is the
same mistake that the previous commit corrected in D_B, D_T and D_X, where the
thresholds turned out to exist. Both now say the question is unchecked and that
none is supplied here. The rho docstrings are softened to "no published threshold
was found" for the same reason.
calcDVJopek also stated that the paper defines the weights as "the reciprocal
dispersions", which is more specific than anything that could be checked, and
contradicted the commit message that introduced it. It now says the weights are
defined in terms of the dispersions and that the definition could not be read.
calcDR gains the opposite correction: Valsecchi et al. recommend no threshold, but
Jenniskens (2008) reports D_N < 0.20, and since D_R <= D_N that is a necessary
condition on D_R. Worth a reader knowing.
Argument order
calcDX took (sol, ra, dec, vg), following the order of the paper's equation, while
calcDN, calcDR and calcDV all take (ra, dec, sol, vg). Every argument is a float,
so calling one with the other's order returns a plausible wrong number in silence.
calcDX now matches the rest of the module, with a docstring line saying why it
departs from the paper's ordering.
A test that could pass while being wrong
test_DT_reproduces_the_jenniskens_tisserand_values recovered a Tisserand parameter
as 3.0 + D_T against a reference orbit of T = 3. D_T is an absolute difference, so
that only holds for T > 3. Both objects in the test are above 3 so it passed, but
for 21P/Giacobini-Zinner, whose tabulated T_J is 2.47, the construction returns
3.53. It now checks the parameter against the table directly and checks separately
that D_T is the difference of two tabulated values.
Missing criterion
isCometaryQi's docstring recommended E-i as one of the two most reliable
two-parameter criteria, and E-i was not implemented. Added as isCometaryEi, with
calcOrbitalEnergy, GAUSS_K_SQUARED and JW_ENERGY_LIMIT. The published limit of
-5.28e-05 corresponds to a cut on the semi-major axis alone at 2.8022 AU, which
the test asserts. The eccentricity is unused and kept only so the five criteria
share a signature.
Consistency
- D_B's dispersions were inline literals while D_X's weights and D_ACS's scale
were named constants. They are now DB_SIGMA_C1, DB_SIGMA_C2 and DB_SIGMA_C3.
- The 1.5 and 2 in D_V, the two coefficients that could not be verified, now
carry a comment at the point of use rather than only a note in the PR.
- calcDSAC returned "[float] D value" where every sibling names the criterion.
- JW_APHELION_LIMIT and JW_KRESAK_P_LIMIT had no units.
- Two inline "import inspect" in the tests moved to the top of the file.
- Four line continuations inside already-open parentheses removed.
thresholdReliability now documents its cost, which grows as the number of trials
times the square of the number of orbits; a few thousand orbits is not practical
and wants a chunked search instead.
One comment removed rather than reworded: the note above the D_X weights repeated
the paper's description of them as normalising each term's contribution. The
commit that added them recorded a measurement that this does not hold, so the
comment now says the weights are reproduced as published values rather than as a
normalisation to rely on.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
fd328c7 to
149d51f
Compare
Closes the second of the two omissions recorded when this branch was opened.
Tancredi (2014), Icarus 234, 66, doi:10.1016/j.icarus.2014.02.013, classifies a
single orbit as cometary or asteroidal, so it belongs in OrbitClassification.py
alongside the Tisserand and Kresak criteria.
New: classifyTancrediComet, classifyTancrediAsteroid, isTancrediResonanceProtected,
calcHillRadius, calcResonanceSemiMajorAxis, plus the constants the scheme needs.
What the criterion adds over a Tisserand cut
Applying the cometary Tisserand range to the asteroid population returns 7332
candidates, of which over 97% are on stable orbits that never approach a giant
planet. Two further conditions do the real work: the body must not be held away
from Jupiter by a mean-motion resonance, and its orbit must actually reach the
planet, expressed through the minimum orbital intersection distance. With those
the paper reports a false positive rate of 0.19 and a false negative rate of 0.05.
The upper Tisserand limit is 3.05 rather than 3, because Jupiter's orbit is not
circular and because encounters out to a few Hill radii still perturb an orbit.
Verification against the paper's own numbers
- eq. 3 reproduces the semi-major axis at the centre of all eight resonances of
table 1 to 7e-04, against a table quoted to three or four figures.
- eq. 2 reproduces the Hill radii quoted for all four giant planets to 2e-03:
Jupiter 0.3551 against 0.355, Saturn 0.4377 against 0.436, Uranus 0.4688
against 0.469, Neptune 0.7752 against 0.776.
- 233P/La Sagra, the one new "Comet" in an Asteroidal Orbit the paper reports,
computes T_Jup = 3.0860 against the quoted 3.086 and classifies as
'asteroidal orbit'.
- The quasi-Hilda orbit the paper gives to justify the 3.05 limit, a = 4.05 AU
with e = 0 and i = 0, computes T_Jup = 3.0492 against the quoted 3.05 and
falls just inside the Jupiter family class, as intended.
- 2060 Chiron classifies as a Centaur and 2P/Encke as Jupiter family, both as
the paper has them.
- The Hildas, in the 3:2 resonance, are recognised as resonance protected and
so are not returned as asteroids in cometary orbits, which is the case the
filter exists for.
Two limits, both stated in the docstrings
The two minimum orbital intersection distances are arguments rather than computed
here. Computing a MOID is a separate problem and the paper's method for it is in a
supplementary appendix that was not available. No MOID routine exists elsewhere in
wmpl either. They are taken in units of the relevant planet's Hill radius, which is
how the paper's own figures express them, and calcHillRadius is provided for the
conversion.
The libration widths used for the resonance test are the maxima tabulated in table
1. The paper computes a width that varies with eccentricity, by a method given in
the same unavailable appendix, so an orbit near the edge of a resonance may be
called protected here when the paper would not. Named in the docstring so the
difference is visible at the point of use.
One further note on the paper: the second form of eq. 1, which rewrites the
Tisserand parameter in terms of q and Q, is missing the planet's semi-major axis
inside the radical as printed and evaluates to 4.689 where the first form gives
3.030. The first form is the one implemented, in calcTisserand, and is unaffected.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The supplementary appendices became available, so the two approximations noted
when the Tancredi classification was added are gone. The criterion now runs from
orbital elements alone.
Appendix A: the minimum orbital intersection distance
calcMOID minimises the distance between a point on each orbit over the two
eccentric anomalies. That surface carries more than one local minimum, so the
search is restarted from the three further points the appendix prescribes and the
smallest of the four results is taken. calcGiantPlanetMOIDs wraps it over the four
giant planets and returns each distance in units of that planet's own Hill radius,
which is the unit the criterion compares against, closing the loop that previously
required the caller to supply both MOIDs.
Verified on the orbits of figure A.1, which the paper chose because the surface has
a second minimum that a single search falls into: the four-start search returns
0.240830 AU against 0.240833 from a 600 by 600 grid over both anomalies. Two
coplanar circles of radius 1 and 1.2 give exactly 0.2, an orbit is at zero distance
from itself, and the result does not depend on which orbit is given first.
Also added calcMaxPerihelionForTisserand and calcMinMOIDForTisserand, which trace
the edge of the forbidden region in the Tisserand against MOID plane. Eq. A.1 is
solved directly rather than through the cubic of eq. A.3 that it squares to, since
that cubic carries two spurious roots: at T = 3.3 its roots are 1.937, 0.551 and
0.234, of which only the middle one satisfies the original equation. The bound
closes to zero at T = 3, where the orbit just touches the planet's, and widens
above it, both asserted.
Appendix B: the libration widths
calcResonanceWidth computes the half width of a resonance from the eccentricity
through the pendulum model, replacing the tabulated maxima used as a fixed width
before. Supporting it are calcLaplaceCoefficient, evaluated from its integral
definition rather than the truncated series of eq. B.8 so that it stays exact as
alpha approaches 1, calcLaplaceDerivative using the Murray & Dermott recursions,
and calcDisturbingFunctionTerm for the four orders of table B.1. The coefficients
depend only on the resonance, so they are cached.
Verified against the widths tabulated in table 1 of the main text, evaluated at
the top of the eccentricity range the paper considers reliable:
resonance order computed table 1 ratio
4:1 3 0.00754 0.0075 1.005
3:1 2 0.02873 0.0287 1.001
5:2 3 0.02598 0.0260 0.999
7:3 4 0.02146 0.0215 0.998
2:1 1 0.11274 0.1127 1.000
One reading of eq. B.6 had to be settled to get the last of those. Its final term
divides by j2, the second integer of the critical angle, which is negative since
j2 = -p. Taking its magnitude gives 0.10927, 3% below the tabulated value, and
would require e = 0.3183 to match, outside the stated range. Carrying the sign
through gives 0.11274 at e = 0.3, the same eccentricity that reproduces the other
four. The signed reading is therefore the intended one, and the code says so where
it is used.
The eccentricity is held to [0.01, 0.3] as the appendix requires, since the
expansion diverges below and is unreliable above; outside that range the width is
held at the value on the nearer edge, which is asserted.
The three outer resonances keep the fixed widths the paper adopts for them rather
than computed ones, which is the paper's own choice where the expansion no longer
holds, not an approximation introduced here.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Closes the last unverified formula in this branch. Jopek, Rudawska & Bartczak (2008), EM&P 102, 73, doi:10.1007/s11038-007-9197-8, became available, and it changes the implementation as well as the documentation. A real error: the units were wrong The paper defines the vectorial elements "in the units AU, AU/day and the mass of the Sun", with mu = k^2. This module computed them with mu = 1, which is self-consistent but puts the angular momentum and the energy on a different scale from the one the published weights were measured on. Since the weights are dimensional, the two cannot be mixed. The angular momentum now carries a factor of k and the energy a factor of k^2. How far off that was: for a Geminid-like orbit the paper's tabulated energy dispersion is 0.91% of the energy in the paper's units, and 0.00027% of it in the units used here before. Only the first is a credible spread. The weights are published, and are now the defaults Eq. 5 defines them as w = (2*sigma)^-2, the reciprocal squared dispersion of each element within a stream, so a pair differing by twice the dispersion in one element contributes exactly 1 to the sum. That is what fixes the scale of the criterion and makes its thresholds meaningful. Table 1 gives the dispersions for a stream at five ages and for the sporadic background, all now in DV_DISPERSIONS, with calcDVWeights turning any of them into weights. The default is the 4000 year set, which is what the paper used for its own search. w_h, w_e and w_E become keyword arguments rather than required ones. The earlier docstring said the paper defined the weights as reciprocal dispersions, then a later commit softened that to "in terms of the dispersions" because it could not be checked. The original wording was the closer of the two. The 1.5 and the 2 are deliberate, not transcription noise Both were flagged here as unverified. The paper states the reason: the energy is invariant under the principal secular perturbation and the third angular momentum component is semi-invariant, so it weights those two up beyond what their dispersions alone would give. Thresholds are published Table 2 gives them at the 99% reliability level against the smallest accepted group size, now in DV_THRESHOLDS. The docstring previously said whether any threshold was published was unchecked. The column is headed "D_V x 10^-1", which has to be read as the printed figures being D_V scaled down by ten, so the thresholds run from 2.414 to 3.128 rather than 0.2414 to 0.3128. Simulating pairs drawn from one stream using the dispersions of table 1 settles it: 97.1% of same-stream pairs fall below 3.128, which is what an association threshold at 99% reliability should do, while 0.0% fall below 0.3128. Both readings are asserted in the tests so the choice is visible. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The reference list of Jopek, Rudawska & Bartczak (2008) identifies the volume as the proceedings of the US/European Celestial Mechanics Workshop held in Poznan in July 2000, pages 365 to 366, and dates it 2001 where other citations give 2002. Both are now recorded, since the volume itself is still not accessible here and the discrepancy is worth a reader knowing. That paper also describes the criterion in its own words, as the difference of the orbital momentum vectors per unit mass of two orbits, which is what is implemented here. That is corroboration from a second source rather than verification against the original, and the docstring keeps saying so. Worth noting for anyone chasing the Drummond reference: the same reference list gives Icarus 47, 500 to 517 for Drummond, which is "Earth-orbit-approaching comets and their theoretical meteor radiants", not the paper that defines D_D. That is Icarus 45, 545 to 553. A third source getting the Drummond citation wrong. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The right-ascension term of D_X put cos(dec1) inside the sine argument rather than multiplying the sine. Rudawska et al. (2015) eq. 2 writes the term as [2 sin((a1 - a2)/2) cos(d1)]^2, where cos(d1) scales the chord. The two forms agree to first order in the radiant separation, which is why no test in the suite could tell them apart: on the PR's own Taurid-branch case the difference is 0.00%. They diverge for widely separated radiants, reaching +41% at 180 deg of right ascension and 60 deg of declination, so the old form systematically overstated D_X for dissimilar pairs. A new test compares the isolated term against the published closed form at a separation large enough to distinguish them, and fails on the previous form. Two guards in the Tancredi classifier: - calcTisserand returns nan for an unbound orbit, where the parameter is not defined. Every comparison against nan is False, so classifyTancrediComet reached 'unclassified' by falling through every branch rather than by recognising the case. It now tests for it, and calcTisserand documents the nan, pointing at tisserandParameterJupiter as the scalar equivalent that returns None instead. - The class intervals were open at both ends, so an orbit landing exactly on T = 2 or T = 3.05 matched no branch and came back 'unclassified'. They are now half open. Documentation corrections found while checking the formulas against their sources: - The 3 AU scale of D_ACS is printed in eq. 2 of Asher, Clube & Steel (1993), which reads D^2 = ((a1 - a2)/3)^2 + (e1 - e2)^2 + (2 sin((i1 - i2)/2))^2. It is a published constant, not one inferred from their table 1. - calcC builds the angular momentum as sqrt(p) while calcDVJopek builds it as k*sqrt(p), so the two are on scales differing by the Gaussian constant. The convention Neslusan uses could not be established, which matters for anyone comparing a C value against a published one. - calcMOID takes the semi-major axis where every criterion in this module takes the perihelion distance, with otherwise identical signatures. - calcGiantPlanetMOIDs returns a dictionary, not the two scalars classifyTancrediAsteroid expects. - calcDR takes scalars only, since calcVgComponents is written with the math module; and its w1 keyword is a term weight, not an argument of perihelion. - The HILL_RADIUS_* constants are the paper's values rather than output of calcHillRadius, which is why that function has no call site. Comments added to the functions carrying bare transcribed coefficients: the table B.1 polynomials, the Laplace coefficient recursion and its cache, the four Powell restart points of the MOID search, and the 3-1-3 rotation. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ction Dcriteria.py imported A_JUPITER, GAUSS_K and GAUSS_K_SQUARED from OrbitClassification.py, whose own docstring says the two modules are kept apart, and dragged scipy.integrate and scipy.optimize into the import of a module that needs neither. The three constants now live in wmpl/Utils/OrbitConstants.py, which imports nothing, and OrbitClassification re-exports them so existing imports keep working. Dcriteria imported on its own no longer loads OrbitClassification or scipy. The Gaussian gravitational constant was written out in four places. Orbit.py had it inline as a bare literal and MeanOrbit.py defined its own copy; both now take it from the shared module, MeanOrbit keeping GAUSS_GRAVITY_CONST as an alias for the callers that import that name. Jupiter's semi-major axis is deliberately NOT unified, which is worth recording because it looks like an oversight. OrbitClassification uses 5.20336 AU and REBOUND.py and Orbit.py use 5.204267 AU. Substituting the latter moves the 1:1 resonance to 5.2043 AU and misses the 5.203 that Tancredi (2014) tabulates, so the test reproducing that table fails. The two values belong to different sources; the difference is 1.7e-4 relative and shifts a Tisserand parameter by about 1e-4, far below any class boundary. Both constants now say so. tisserandParameterJupiter and calcTisserand now cross-reference each other, along with the two classifiers built on them, which split at 3 and at 3.05 and return different vocabularies. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…holds Three pages in wmpl/Docs, following the format of the REBOUND manual, plus the index entry for each. OrbitSimilarity.md covers the sixteen pairwise criteria: a table giving each one's inputs, whether it is a metric, its published threshold or the absence of one, and its source; then sections on the classical family, the Kholshevnikov metrics, the geocentric criteria and the two Taurid Complex forms. The pitfalls section carries the things that actually cost people results: that thresholds transfer neither between criteria nor between datasets, that only the rho family satisfies the triangle inequality, that D_D divides by zero for circular orbits, and that a self-comparison lands near 1e-8 rather than at zero. OrbitClassification.md covers the single-orbit criteria and the Tancredi scheme, and points out where the repository already has a Tisserand implementation with a different Jupiter constant and a different class vocabulary. It also says plainly which parts are expensive, since calcMOID runs Powell four times and calcGiantPlanetMOIDs calls it four more. DThresholds.md leads with the warning that the traditional Southworth & Hawkins threshold has a measured coincidental-pair probability of 1.0 for bolide samples, so it cannot support a claim of association, and points at thresholdRandomPairing instead. Every page ends with a verification table recording which formulas were checked against a primary source, which were confirmed only indirectly, and which could not be reached at all. All eight code examples were executed against the working tree. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Review pass: 4 commits pushed to this branchReviewed with every formula checked against its primary source where one could be reached. 78/78 tests passed as submitted; 214 pass now across Utils, Rebound and MetSim. The headline: One real bug, in Rudawska et al. (2015) eq. 2 writes the right-ascension term as term_ra = w_ra*(d_vg + 1.0)*(2*np.sin((ra1 - ra2)/2.0*np.cos(dec1)))**2
# ^ cos(dec1) inside sin()The two agree to first order in the radiant separation, which is exactly why the suite could not catch it:
All 78 tests still passed with it fixed, and on your own Taurid-branch case the difference is 0.00%. So the claim that every criterion was checked against a paper value does not quite hold for this one — the within-shower regime cannot distinguish the forms. In fairness to the practical impact: the error only grows for widely separated radiants, which sit far above the 0.15 merge threshold anyway, so shower-search outcomes are unlikely to change. One claim in the description is more favourable than you thoughtThe D_ACS scale of 3 AU is published. The description devotes a section to inferring it from table 1 via a bounding argument. I read eq. 2 from the ADS scan of Asher, Clube & Steel (1993):
It is printed right there. The elaborate inference was unnecessary — and it happens to have landed on the correct value.
|
| Source | Outcome |
|---|---|
| Kholshevnikov et al. (2016) | Read — ρ₁, ρ₂, ρ₅ verified |
| Asher, Clube & Steel (1993) | Read (ADS scan) — eqs 1 and 2 verified, including the 3 AU scale |
| Jopek & Bronikowska (2017) | Read (arXiv:1609.03968) — all 12 threshold coefficient pairs and the validity ranges verified exactly |
| Courtot, Shober & Vaubaillon (2026) review | Read — D_SH, D_D, D_H, D_R, D_N, D_B, D_T, D_V and D_X verified, including D_V's asymmetric 1.5× on h_Z and 2× on the energy term |
| Tancredi (2014) | Unreachable. Paywalled; the author's own site no longer resolves and the Wayback Machine has no capture. The 3.05 limit and the class structure are corroborated by a 2025 paper Tancredi co-authored, but table 1, the Hill radii, table B.1 and eqs A.1 and B.5–B.7 are unverified. The implementation reproduces the tabulated resonance centres and widths to the printed precision, which is strong indirect evidence, not a check. |
| Jenniskens (2008), Icarus 194, 13 | Unreachable. So I cannot confirm or refute the description's claim that its eqs 3, 5 and 9 contain three typos. That claim is currently unsupported in the PR as well, and worth softening or sourcing. Also: Icarus 194, 13 is titled "Meteoroid streams that trace to candidate dormant comets"; "Mostly dormant comets…" is a different 2008 Jenniskens paper in EM&P 102, 505. Worth checking which one the formulas came from. |
| Neslušan (2002) | Unreachable. Springer proceedings chapter, paywalled. The k-factor question above stays open. |
Two smaller notes
- Test naming. This PR and Fix the D_H node sign convention and guard the acos domain in Dcriteria #96 use
test_snake_case; every other test in wmpl usestestCamelCase. Renaming 78 + 7 tests is your call, so I left them and matched the local style in the ones I added. - None of the twelve new criteria are wired into the
__main__CLI, which still exposes only D_SH, D_D and D_H. Probably deliberate, but worth a decision.
For the record, I also checked #96 against this: after both merge there is no inconsistency. Π₂₁ and the ρ family's angle P are two different, well-defined angles related by cos P = cos u₁ cos u₂ + sin u₁ sin u₂ cos I₂₁, each correct for its own criterion, and #96's fix makes calcDSH reproduce Jopek's mutual-node definition to 3e-11 rad over 200,000 random pairs.
🤖 Generated with Claude Code
|
@AstroEloy — could you review the four commits I pushed to this branch ( Please check these three, where I changed behaviour or contradicted the description:
Two open questions I could not close:
Things I changed that you may simply disagree with:
The branch also merges current master, so it carries the 🤖 Generated with Claude Code |
D_SH and D_H choose the branch of an arcsine from the raw difference of the two ascending nodes, so the convention holds only when both are given in [0, 360). A node written as 710 rather than 350 selects the other branch and returns a different value. This is the limitation documented by the companion fix in wmpg#96, and it belongs here because this is the page a user reads before choosing a criterion. The criteria built on vectors instead of an arcsine, the rho family and calcDVJopek, have no branch to choose. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
71764f7 added test_DX_right_ascension_term_scales_the_chord_not_the_angle, test_tancredi_class_limits_are_half_open and test_tancredi_unbound_orbit_is_not_silently_unclassified, but did not add them to the list the module's standalone runner iterates over. pytest discovers tests by name and ran all three; the standalone path the module docstring documents ran neither, so it reported 64 tests where 67 exist. The one that matters most is the first: it is the regression test for the D_X right-ascension fix in that same commit, and the form it guards against is the one that was in the tree. All three pass; the runner now reports 67. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Checked all four commits. The review is right on the substance, including on the First: three of the tests added in
|
| form | agreement |
|---|---|
cos((i₁+i₂)/2), as in this codebase |
100.00% |
cos((i₁−i₂)/2), as printed in eq. 3 |
0.00% |
So no change is warranted, and the claim is not unsupported — it just was not
sourced in the description. Happy to add the section and equation numbers there if
you want it on the record in the PR rather than only in this thread.
The author's own criteria in that paper, D_B and D_T, are correct. It is only
its restatement of prior work that is unreliable, which is an argument for the
approach this PR took of verifying each formula against the paper that introduced
it.
5. The Neslušan k-factor — agreed, stays open
Your documentation of it is the right outcome. The one thing I can add is negative:
Jopek, Rudawska & Bartczak (2008) describe the criterion in their introduction as
"the difference between the orbital momentum vectors (per unit mass) of two orbits"
without fixing the units, so that paper does not settle it either. The 2000
proceedings chapter remains the only source that would.
6, 7 and 8 — still with @AstroEloy
These are calls I should not make on his behalf, so flagging rather than answering:
whether to split the OrbitConstants layering into its own PR, whether to rename
the 81 tests here and the 7 in #96 to testCamelCase, and whether to expose the
twelve criteria in the __main__ CLI.
On 6 specifically, the description has been corrected either way: it no longer
claims a purely additive diff and now states the real contribution against
upstream/master as 13 files, +4467 / −3.
81 tests pass here, and the branch still merges cleanly.
🤖 Generated with Claude Code
The CLI computed D_SH, D_D and D_H while the module now defines sixteen criteria. All of them are now reported, grouped by what they need, along with the published threshold for each or a note that none exists. Criteria on the orbital elements, always computed: D_SH, D_D, D_H, rho_1, rho_2, rho_5, C, D_V, D_B, D_T, D_ACS and D_SAC. The semi-major axis that D_ACS and the classification take is derived as q/(1 - e). Criteria on the geocentric radiant and speed, when those are available: D_N, D_R and D_X, plus the Vida D_V when a corrected heliocentric direction is present. They come from a second trajectory file, or from the new --ra, --dec, --sol and --vg arguments. The four have to be given together; giving some of them warns and skips the group rather than computing a wrong number from defaults. The dynamical classification of each orbit is reported too, side by side: the Tisserand parameter, Kresak K and P, the aphelion distance, the four Jopek & Williams two-parameter tests and the Tancredi class. Two details that would otherwise bite: The orbit stores the geocentric and heliocentric speeds in m/s while every criterion here takes km/s, so both are divided by 1000 on the way in. Running the same pair through the two paths, once from a pickle and once through --vg in km/s, gives identical D_N, D_R and D_X, which is what checks the conversion. The classification functions are imported inside __main__ rather than at module level, so that importing Dcriteria still does not pull in OrbitClassification, the decoupling introduced in 97e31b4. Verified: OrbitClassification is still absent from sys.modules after importing Dcriteria. Also fixed: traj_path2 was a required positional, so the manual entry its own help text describes was unreachable, and the code that guards it with "is not None" could never take that branch. It now takes nargs='?', which is what the rest of the block was already written for. The dead code after sys.exit() at the end of the block is left alone. Exercised in four modes: two trajectory files, one file with manual elements, one file with manual elements plus the four radiant arguments, and a partial set of radiant arguments. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Decisions on 6, 7 and 8, from @AstroEloy. 6 — the constants work stays hereNo separate PR. Keep One correction to the justification, though, because it does not hold as stated.
The description has already been corrected: it no longer claims a purely additive 7 — test names stay as they areYour note says you "matched the local style in the ones I added", but the three Given that, renaming 88 names buys nothing functional and the files are internally 8 — every criterion is now in the CLI (
|
|
WIP. I will:
|
Take both orbits from wherever the user has them. The first orbit is now either a trajectory pickle or --q1 --e1 --i1 --peri1 --node1, exactly as the second one already was, so the tool works on published elements with no pickle in sight. The unnumbered -q -e -i -p -n keep working as aliases of the second orbit's arguments, so existing command lines are unaffected. Drive the printing from a registry of (key, label, callable), which gives -c/--criterion its list of keys for free. Selecting a subset matters once sixteen criteria are printed and only one is wanted. Print no thresholds. The community has not settled on one way to derive them: the value depends on the number of orbits involved, and for a single pair it is not well defined at all. Printing a number next to each criterion implies a decision this tool is not entitled to make. The candidate methods survive as a comment above the registry, and wmpl.Utils.Dthresholds implements them for a caller who has a sample to derive one from; nothing here imports it. Add --no-classification to skip the dynamical classification, and --quiet to print one 'KEY VALUE' line per criterion and nothing else, so the tool can be called in a loop. OrbitClassification is imported inside __main__, so that importing Dcriteria as a module still does not pull in scipy through it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Reading the paper itself rather than a summary of it settles three things. The K criterion is Whipple's (1954), eq. 3 of the paper, not Kresak's; Kresak contributed P and Q. Rename calcKresakK to calcWhippleK so the attribution in the name is right before anything depends on it. The form with the semi-major axis is confirmed: the paper offers Pribram and Neuschwanstein, two meteorites of asteroidal origin, as the case where K fails, quoting K ~ 0.08, and only this form reproduces that. Record the paper's own caveat, that K is inconclusive for short-period orbits of low eccentricity, in the docstring: it produced 16.4 per cent exceptions among near-Earth asteroids against 3.9 for Q, so an orbit that lands near zero has not been classified. The T-i limit of 0.58 is reconciled. The paper's eq. 10 defines T as 1/a + 2*a_J^-1.5*sqrt(a*(1 - e^2))*cos(i), which is the Tisserand parameter divided by a_J and so carries units of 1/AU. Scaling by a_J puts the limit at 3.018, the familiar Jupiter family cut, and comparing a value of calcTisserand against 0.58 as printed would have called every orbit cometary. With the limit settled, add isCometaryTi, which completes the five criteria of the paper, and print T-i alongside the other four. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Classifying one orbit meant running Dcriteria against a second orbit that had nothing to do with the question, and reading the classification block out of its output. Give the module its own command line instead. It takes the orbit from a trajectory pickle or from elements, and accepts the size as either a perihelion distance or a semi-major axis, since the criteria are written in terms of a and asteroid catalogues quote it while meteoroid work quotes q. The node and the argument of perihelion are optional: they are needed only for the distances to the giant planets, and so for the Tancredi classification of an asteroid, which is skipped without them. Stop on an unbound orbit rather than compute a semi-major axis from it. Every criterion here is a function of a, and dividing by 1 - e with e above 1 would print numbers that read as classifications. --quiet prints one 'KEY VALUE' line per quantity, matching the Dcriteria command line, so the two can be called the same way in a loop. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Dcriteria and ParentBodySearch each named the criteria their own way. Dcriteria offered sixteen keys through -c and printed labels like D_SH; ParentBodySearch had an if/elif over three lowercase strings and printed whatever the user typed next to the column, so -d dsh produced a column headed 'D crit dsh' and a typo silently produced D_SH results under the typo's name. Move the registry of criteria on the orbital elements out of the Dcriteria command line to module level and add getElementCriterion, so that both modules read the same list. A key now means the same criterion and prints the same label in both, and ParentBodySearch reaches all twelve element criteria rather than three. The command line stops on a key it does not know instead of relabelling a D_SH run; findParentBodies keeps its warn and fall back, so a caller sees no change. The registry passes the two orbits as (q, e, i, node, peri) tuples, since the criteria do not agree on an argument order: some read all five elements, D_B drops the perihelion distance and D_ACS reads the semi-major axis instead. A test pins each packing against the direct call, because a mistake there would be silent. D_ACS made the semi-major axis from q/(1 - e), which raises on the roughly 1800 orbits of exactly e = 1 in the comet catalogue that ParentBodySearch sweeps. Give it infinity instead, which is the right value for a difference of semi-major axes against an unbounded orbit, and skip bodies whose criterion is not finite so they never rank as a match. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The keys and labels live in one place and are read from two. Nothing at either end said so, so an edit to a label in Dcriteria would quietly change what ParentBodySearch accepts on -d and prints above its results column. Put a short warning where someone about to make that edit will be reading: the module docstring of ParentBodySearch, its findParentBodies argument, and getElementCriterion. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
This branch is done from my side. While working on this I also explored whether source-region models (NEOMOD, METEOMOD) could be plugged into the dynamical classification here or in a follow-up. Notes for whoever picks this up next:
|
Brings in the D_H node sign fix and the acos domain guards merged as wmpg#96. The branch was five commits behind, so its own command line reported the pre-fix D_H: PR wmpg#96's worked example (q = 0.5, e = 0.7, i = 15 deg, O1 = 10 deg, w1 = 150 deg, w2 = 190 deg, O2 = 350 deg) came out as 0.698722 where master gives 0.266746. That is now consistent. Merges without conflict; 223 tests pass on the result.
|
@AstroEloy — checked all of it. You are right on both counts against me, and the branch is now up to date with master. You found a real defect in my commitsConfirmed exactly: 64 registered, 67 defined, and all three missing were mine — including the regression test for my own And the scipy claim was misleadingReproduced your result:
What I verified of your new work
The On the Jenniskens point: you are right that
|
…ySearch docstrings Master now carries wmpg#101 (repo-wide spelling fixes) and wmpg#102 (Sun passages as close encounters). The only conflicts were two docstrings in wmpl/Utils/ParentBodySearch.py, where wmpg#101 corrected "comapring" and "heighest" in text that this branch had rewritten. Resolved by keeping this branch's text with both spellings corrected. Everything else merged automatically. Verified on the merged tree: 235 tests pass under pytest (wmpl/Utils/Tests and wmpl/Rebound/Tests), the standalone runners report 10, 72 and 14, and codespell finds nothing new in the changed files. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
Master (now with #101 and #102) merged into this branch again. The only conflicts were two docstrings in On the merged tree: 235 tests pass under pytest, the standalone runners report 10 / 72 / 14, and codespell finds nothing new in the changed files (its only hits are the astronomical "precess", which is pre-existing on master and not a typo). 🤖 Generated with Claude Code |
Adds the orbit dissimilarity criteria the literature offers besides
D_SH,D_D,D_HandD_N, plus two new modules: one for criteria that classify a singleorbit, and one for choosing a criterion's threshold.
No existing D criterion is modified.
calcDSH,calcDH,calcDD,calcVgComponents,calcDN,calcDVandcalcDVuncertare byte-identical tomaster, and the__main__block is untouched.The branch is no longer purely additive, and an earlier revision of this
description wrongly said it was. The review pass added
wmpl/Utils/OrbitConstants.pyand touched
Orbit.py,MeanOrbit.pyandREBOUND.pyso thatDcriterianolonger imports
OrbitClassification(and with it scipy), and so the Gaussianconstant is defined once. Current contribution against
upstream/master:13 files, +4467 / −3. Whether that layering work should be split into its own
PR is still open — see the review thread.
Branches from
master, not from the companion bug-fix PR, so the two areindependent.
New in
Dcriteria.pycalcRho1,calcRho2,calcRho5calcCcalcDRcalcDB,calcDTcalcDXcalcDVJopekcalcDACScalcDSACThe
rhometrics are the only criteria here that satisfy the triangle inequalityand the only ones defined for circular orbits. Both properties are tested with
D_Das the control: the triplebreaks the
D_Dtriangle inequality by 0.0679, whilerho_2has 0.2298 of slackon the same triple.
D_Dalso raisesZeroDivisionErrorfor two circular orbits,which the metrics handle.
calcDV, the unpublished Vida criterion already in the module, keeps its name sothe public API does not break; the new Jopek function is
calcDVJopek.All new functions are written against
numpyrather thanmathand acceptscalars or arrays, which also removes the branch construct that produced the
D_Hnode sign bug in the companion PR.New module
OrbitClassification.pyThese take one orbit and return a dynamical class, so they do not belong among the
pairwise criteria:
calcTisserand(Whipple 1954),calcKresakK,calcKresakP,calcAphelionDistance,calcOrbitalEnergy, and the two-parameter criteriaisCometaryQi,isCometaryEi,isCometaryKi,isCometaryPifrom Jopek &Williams (2013), MNRAS 430, 2377, eqs 8 to 12. Q-i and E-i are the two the paper
found most reliable.
New module
Dthresholds.pythresholdDr(Southworth & Hawkins 1963; Lindblad 1971),thresholdBreakPoint(Neslusan, Svoren & Porubcan 1995),
thresholdReliability(Jopek, Valsecchi &Froeschle 1999) and
thresholdRandomPairing(Jopek & Bronikowska 2017, P&SS 143,43), whose threshold depends on the D function and the population as well as on
the sample size. Its twelve published coefficient pairs are exposed as
RANDOM_PAIRING_COEFFS; an unpublished combination raisesValueErrorratherthan substituting a default.
Worth flagging for users of
thresholdDr: Jopek & Bronikowska measured theprobability of a coincidental pair at those thresholds as 1.0 for bolide samples
of 200 to 800 orbits, i.e. a "similar" pair is certain whether or not any orbits
are related. It runs 7 to 9 times looser than the threshold that delivers 0.01.
That is recorded in its docstring.
Thresholds are stated per criterion, never invented
Each docstring carries the criterion's published threshold or states that none was
published. Seven of the papers publish none, so no default is supplied. They would
not transfer anyway:
rhocarries units ofsqrt(AU)withL = 1 AU, whileD_SHandD_Dare dimensionless, and the random-pairing thresholds differ by afactor of about 2.4 between
D_SHandD_Dat fixed sample size.Verification
81 tests across
test_DcriteriaAdditional.py(67) andtest_Dthresholds.py(14).Each criterion is checked against a value tabulated in its own paper, or, where
none is tabulated, against an analytic identity it must satisfy by construction.
Paper values.
rho_1,rho_2andrho_5reproduce table 2 of Kholshevnikovet al. (2016) for 96P/Machholz 1 and 2003 EH1 at 7415 BC:
rho_1rho_2rho_5The residual is set by the precision of the published elements: perturbing them
within the half-width of their quoted rounding moves
rho_2over[0.0354, 0.0360]. The test tolerance is 0.002, and every plausible transcriptionerror of the formulae was measured to miss by at least 0.0099, five times that —
flipping the sign of
Deltaincos Pgives 0.0450, dropping thee1*e2termgives 1.1875, using
cos Pin place ofcos Igives 0.0475.The paper's closed form for
cos Pwas also checked independently against the dotproduct of the two perihelion unit vectors, agreeing to 5.4e-16 over 20000 random
pairs.
thresholdRandomPairingreproduces eq. 14 of Jopek & Bronikowska as tabulated intheir column
D_(14)to 7.7e-05, and the nine large-sample formulae track theirtable 7 to 6.4e-04 absolute and 5.0% relative over 200 to 16000 orbits (they are
least-squares fits through those points, so exact agreement is not expected). The
criterion ratios of their table 8 follow from the formulae alone and match all six
published averages to two decimals.
thresholdDrreproduces their columnD_(1)to 4.2e-05, inside the 5e-05 rounding floor of a four-decimal column.
calcTisserandgives 3.026 for 2P/Encke against the literature 3.03, and exactly3 for an orbit identical to Jupiter's.
The
calcDACSscale is published, and independently cross-checked here. Asher,Clube & Steel (1993) print it in eq. 2 itself, which reads
D² = ((a₁−a₂)/3)² + (e₁−e₂)² + (2 sin((i₁−i₂)/2))². An earlier revision of thisdescription presented the argument below as a determination of the scale, which
overstated it — the value is published, not inferred.
It is worth keeping as a cross-check, because it is independent of the printed
equation and happens to be the only way the paper's own table can be brought to
bear. Table 1 cannot be recomputed from its own columns: the inclination is first
adjusted by Brouwer (1947) secular perturbation theory to the smallest value the
orbit ever reaches, and the eccentricity likewise, while the table reports observed
values. The semi-major axis is not adjusted and the other terms are non-negative,
so its term is a rigorous lower bound on every tabulated
D, and it is attainedexactly by P/Encke and 1991 TB2. Those two facts bracket the scale at 3 AU, in
agreement with the printed value:
Both tests were run against substituted scales to confirm they reject every value
but 3.0. The docstring states that no secular adjustment is applied by the
function, so passing observed elements will not reproduce the paper's table.
Analytic identities, where no paper value exists: the triangle inequality for
the metrics with
D_Das control;rho_5 <= rho_2withrho_5attaining theminimum of
rho_2under a scan over both nodes and both arguments of perihelion;D_R <= D_Nagainst the existingcalcDN;Cagainst the angular momentum termof
rho_1; theD_Venergy term against-1/(2a);D_B'sC3term taking thesmallest angular difference across the 0/360 deg cut; and the two Taurid forms
sharing their eccentricity and inclination terms, with their separately quoted
reference orbits agreeing (
a1(1 - e1) = 0.3780 AUagainst the publishedq1 = 0.375 AU, inside the range implied by the quoted precision).A self-comparison cannot return exactly zero, for the same reason as in the
D_SHfamily: the square of the metric cancels to the 1e-16 rounding level andthe square root lifts that to about 1e-08. Measured floor over 5000 random orbits
is 3.7e-08 for
rho_1, 6.0e-08 forrho_2and exactly 0 forrho_5, so thetests allow 1e-07.
D_B,D_TandD_Xverified against their primary sourcesAn earlier revision of this PR carried caveats saying these three were transcribed
from a secondary source. The primary papers have since been checked and all three
formulae were correct, but the surrounding documentation was not. Corrected in
8389eab:D_X's weights are published, and are now the defaults. Rudawska et al.state them in section 2.2:
w_lambda = 0.17,w_alpha = 1.20,w_delta = 1.20,w_v = 0.20. They were previously required arguments on thebelief that the paper left them undefined. The velocity unit is not ambiguous
either — the paper tabulates
V_gin km/s — and a threshold is published:groups merge at
D_X <= 0.15.D_TandD_Bhave published thresholds. Jenniskens section 3.2:D_T < 0.3withD_B < 1.0identifies parent bodies and siblings;D_T < 0.6withD_B < 1.5also captures bodies related through an earlierfragmentation. Both docstrings previously said no threshold was published.
calcDTnow takesqrather thana, following the paper's eq. 13, whichwrites the Tisserand parameter in
qande"because the observational errorsin these orbital elements are smaller than those in
a". The two forms agree to2.1e-14 over 200000 orbits, and eq. 13 stays finite at
e = 1where the(a, e, i)form evaluates tonan. This also bringsD_Tinto the variablesthe rest of the module works in.
New paper values behind all three:
T_Jcomputes 3.0264 for 2P/Encke againsttable 1's 3.03 and 4.5108 for 3200 Phaethon against 4.51; mean within-shower
D_Xcomputes 0.061 / 0.101 / 0.070 for the Geminids, Perseids and Orionids against the
quoted 0.06 / 0.09 / 0.07; and the tabulated Taurid branch means give
D_X = 0.244, above the 0.15 merge threshold, consistent with the paper reportingthat its method keeps them separate.
A caution on section 2.1 of Jenniskens (2008), which restates the older
criteria — it should not be used as a source for them. Its eq. 9 writes the last
D_Hterm with(e1 - e2)/2where(e1 + e2)/2belongs; its eq. 5 writes theD_Deccentricity term with(e1 - e2)in the denominator; and its eq. 3 writescos((i1 - i2)/2)inPi_21wherecos((i1 + i2)/2)belongs. That last one waschecked against the mutual-node vector definition over 200000 pairs: the form in
this codebase agrees 100.00% of the time and the form as printed agrees 0.00%,
so no change is warranted. The author's own criteria,
D_BandD_T, are correct.D_Vcorrected against its primary source1a2c8fccloses the last unverified formula, and it was a real error, not just adocumentation one. The paper defines the vectorial elements "in the units AU,
AU/day and the mass of the Sun", with
mu = k^2. This module computed them withmu = 1— self-consistent, but on a different scale from the one the publishedweights were measured on, and the weights are dimensional. For a Geminid-like orbit
the tabulated energy dispersion is 0.91% of the energy in the paper's units and
0.00027% of it in the units used before; only the first is a credible spread.
The angular momentum now carries a factor of
kand the energyk^2.The weights are published and are now the defaults. Eq. 5 gives
w = (2*sigma)^-2, so a pair differing by twice the dispersion in one elementcontributes exactly 1 — that is what fixes the criterion's scale. Table 1's
dispersions for five stream ages plus the sporadic background are in
DV_DISPERSIONS, withcalcDVWeightsturning any of them into weights; thedefault is the 4000-year set the paper used for its own search.
The
1.5and2are deliberate, not transcription noise: the energy isinvariant under the principal secular perturbation and the third angular momentum
component semi-invariant, so the paper weights those two up beyond their
dispersions.
Thresholds are published (table 2, 99% reliability), now in
DV_THRESHOLDS.The column is headed
D_V x 10^-1, which has to be read as the printed figuresbeing
D_Vscaled down by ten. Simulating pairs from one stream using table 1'sdispersions settles it: 97.1% of same-stream pairs fall below 3.128, as an
association threshold should, while 0.0% fall below 0.3128. Both readings are
asserted in the tests so the choice is visible to a reviewer.
Remaining provenance caveat
calcC— Neslusan's volume is still not accessible. Its formula iscorroborated by the description in Jopek, Rudawska & Bartczak (2008), which is a
second source rather than the original, and the docstring says so. Whether that
volume publishes a threshold is unchecked; none is supplied.
thresholdBreakPointis implemented as the method is described in theaccessible literature; the 1995 paper's ADS scan carries no text layer, so its
exact formalisation of where the turn is located could not be read. The break is
taken as the bin of strongest negative curvature of the cumulative
distribution, which on a synthetic stream uniform over
[0, 0.05]plus abackground over
[0.30, 1.0]returns 0.04997.thresholdReliabilityconsiders pairs only. Larger minimum group sizes needa clustering algorithm, which is a separate concern from the threshold.
KandPare cited by author and year only. Their definitions andcometary limits are taken from Jopek & Williams (2013) eqs 11 and 12, which was
verified directly; volume and page numbers for the 1967 and 1969 papers could
not be checked and so are not quoted.
Tancredi (2014)
Added in
7a4735b:classifyTancrediComet,classifyTancrediAsteroid,isTancrediResonanceProtected,calcHillRadius,calcResonanceSemiMajorAxis.Applying a bare Tisserand cut to the asteroid population returns 7332 candidates,
over 97% of them on stable orbits. The criterion's value is the two conditions that
follow: the body must not be held away from Jupiter by a mean-motion resonance,
and its orbit must actually reach a giant planet. The paper reports a false
positive rate of 0.19 and false negative rate of 0.05 against direct integration.
Verified against the paper's own numbers: eq. 3 reproduces all eight resonance
semi-major axes of table 1 to 7e-04; eq. 2 reproduces all four quoted giant-planet
Hill radii to 2e-03; 233P/La Sagra, the one new "Comet" in an Asteroidal Orbit the
paper reports, gives
T_Jup = 3.0860against the quoted 3.086 and classifies as'asteroidal orbit'; the quasi-Hilda orbit used to justify the 3.05 limit gives3.0492 against the quoted 3.05; Chiron classifies as a Centaur and Encke as
Jupiter family; and the Hildas are correctly excluded as resonance protected.
The supplementary appendices are now implemented too (
f2ca39a), so the criterionruns from orbital elements alone and the two approximations noted earlier are gone.
Appendix A —
calcMOIDminimises the distance between a point on each orbit overthe two eccentric anomalies, restarting from the three further points the appendix
prescribes because the surface has more than one local minimum.
calcGiantPlanetMOIDswraps it over the four giant planets, returning each distancein that planet's own Hill radii. Verified on the orbits of figure A.1, chosen there
precisely because a single search falls into the second minimum: the four-start
search returns 0.240830 AU against 0.240833 from a 600×600 grid. Coplanar
circles of radius 1 and 1.2 give exactly 0.2, and the result is symmetric.
calcMinMOIDForTisserandtraces the forbidden region, closing to zero atT = 3and widening above it. Eq. A.1 is solved directly rather than through the cubic of
eq. A.3 it squares to, which carries two spurious roots.
Appendix B —
calcResonanceWidthcomputes the libration half width from theeccentricity, with
calcLaplaceCoefficientevaluated from its integral definitionrather than the truncated series so it stays exact as
alphaapproaches 1. Itreproduces all five widths tabulated in table 1:
One reading of eq. B.6 had to be settled for the last of those. Its final term
divides by
j2, which is negative sincej2 = -p. Taking the magnitude gives0.10927, 3% low, and would need
e = 0.3183to match — outside the stated range.Carrying the sign through gives 0.11274 at
e = 0.3, the same eccentricity thatreproduces the other four, so the signed reading is the intended one. Noted at the
point of use.
The three outer resonances keep the fixed widths the paper itself adopts for them,
where the expansion no longer holds — the paper's choice, not an approximation
added here.
A note on that paper: the second form of eq. 1, rewriting the Tisserand parameter
in
qandQ, is missing the planet's semi-major axis inside the radical asprinted, evaluating to 4.689 where the first form gives 3.030. The first form is
the one implemented.
Deliberately not included
Nothing. Every criterion named in the scope is implemented.
Review pass
149d51frecords a review pass over the three modules. No formula changed. Itcorrected one wrong statement about units in
calcRho1; removed two"no threshold is published" claims made about papers that were never accessible
(
calcC,calcDVJopek) — the same mistake that turned out to be wrong forD_B,D_TandD_X; reorderedcalcDX's arguments to(ra, dec, sol, vg)to matchcalcDN,calcDRandcalcDV, since all eight arguments are floats and theprevious order invited a silent wrong answer; replaced a test that recovered a
Tisserand parameter as
3.0 + D_T, valid only forT > 3; addedisCometaryEi,which the
Q-idocstring recommended but which was not implemented; and namedD_B's dispersions andD_V's unverified coefficients at their point of use.Out of scope
No pairwise matrices at survey scale. 10^6 orbits is 10^12 pairs and needs
chunking or a KD-tree pre-filter, which is a separate concern from this module.
🤖 Generated with Claude Code