Name-match example¶
FlyStar’s AlignmentFitCombine class matches stars across epochs by position
(and optionally magnitude). For most fields this works well, but there are
situations where you want to override the positional matching with an explicit
star name:
A fast-moving star has traveled far enough between epochs that it falls outside the normal positional search radius
dr_tol.You have high-confidence external identifications (e.g. from a published catalog) that you want to preserve even when positions are ambiguous.
The name_match option pairs stars by name before or instead of the
positional matching step. This notebook shows when and how to use it.
Contents¶
import numpy as np
import matplotlib.pyplot as plt
from astropy.table import Table
from flystar import starlists, align
1. Overview¶
|
Behaviour |
|---|---|
|
Positions (and magnitudes) only — name columns are ignored. |
|
Named stars are paired first, even if they are farther apart than |
|
Positional matching runs first; names resolve only cases that position + magnitude leave ambiguous. Named stars farther apart than |
Required companion argument¶
Whenever name_match is on you must also supply name_match_dr_tol, the
largest allowed separation (in reference coordinate units) for a same-name
pair. You can give one value that applies to every iteration, or a list with
one value per iteration (the last value is reused for the final matching step):
align.AlignmentFitCombine(
...
name_match='override',
name_match_dr_tol=0.15, # 150 mas if coordinates are in arcsec
)
When to choose 'override' vs 'tiebreak'¶
Use 'override' when:
A star moves fast enough to leave
dr_tolbetween epochs.You have reliable external identifications you want to pin.
Use 'tiebreak' when:
Stars are always within
dr_tol, but crowding causes occasional ambiguous matches that names can resolve without otherwise altering the matching.You want the most conservative use of names (names never override a clear positional match).
2. Override mode¶
The classic case: a fast-moving star has traveled well beyond dr_tol between
two epochs. Without name_match='override' it would be treated as two separate
objects, causing a spurious new entry in the reference table.
We construct a tiny synthetic dataset: one fast star and one slow background star observed at two epochs.
# ── synthetic starlists ───────────────────────────────────────────────────────
# Reference epoch (2010.0): positions in arcsec, magnitudes in K band.
ref = starlists.StarList.from_dict(
name = np.array(['S0-2', 'S0-5' ]),
x = np.array([ 0.000, 0.500 ]),
y = np.array([ 0.000, 0.200 ]),
m = np.array([14.1, 15.3 ]),
xe = np.array([ 0.002, 0.002 ]),
ye = np.array([ 0.002, 0.002 ]),
me = np.array([ 0.05, 0.05 ]),
t = np.array([2010.0, 2010.0 ]),
)
# Second epoch (2022.0): S0-2 has moved 0.15" — well outside a typical dr_tol.
sl = starlists.StarList.from_dict(
name = np.array(['S0-2', 'S0-5' ]),
x = np.array([ 0.130, 0.501 ]), # S0-2 moved ~130 mas in x
y = np.array([-0.075, 0.199 ]), # and ~75 mas in y
m = np.array([14.1, 15.3 ]),
xe = np.array([ 0.002, 0.002 ]),
ye = np.array([ 0.002, 0.002 ]),
me = np.array([ 0.05, 0.05 ]),
t = np.array([2022.0, 2022.0 ]),
)
print('Reference:')
ref[['name','x','y','m']].pprint()
print('\nSecond epoch:')
sl[['name','x','y','m']].pprint()
# ── without name_match: S0-2 is treated as a new star ─────────────────────────
al_pos_only = align.AlignmentFitCombine(
star_lists=[sl],
ref_list=ref,
n_iterations=1,
dr_tol=0.05, # 50 mas — S0-2 has moved 150 mas, so it won't match
dm_tol=1.0,
name_match=False,
verbose=0,
)
al_pos_only.fit()
print('Stars in reference after positional-only alignment:', len(al_pos_only.ref_table))
print(' →', list(al_pos_only.ref_table['name']))
# ── with name_match='override': S0-2 is correctly linked ──────────────────────
al_name = align.AlignmentFitCombine(
star_lists=[sl],
ref_list=ref,
n_iterations=1,
dr_tol=0.05,
dm_tol=1.0,
name_match='override',
name_match_dr_tol=0.20, # up to 200 mas for a name-matched pair
verbose=0,
)
al_name.fit()
print('Stars in reference after name-match alignment:', len(al_name.ref_table))
print(' →', list(al_name.ref_table['name']))
With name_match=False the reference grows to three entries because the
displaced S0-2 is not recognised. With name_match='override' the reference
stays at two, and S0-2’s motion is captured.
3. Tiebreak mode¶
'tiebreak' uses names only to resolve matches that position + magnitude
leave ambiguous. Stars farther than dr_tol are not helped; this mode
is suited for crowded fields where two stars fall within each other’s dr_tol
and their magnitudes are similar, but their names differ.
# Two nearby, similar-magnitude stars that would be ambiguous on position alone.
ref2 = starlists.StarList.from_dict(
name = np.array(['S0-9', 'S0-10' ]),
x = np.array([ 0.000, 0.030 ]), # only 30 mas apart
y = np.array([ 0.000, 0.010 ]),
m = np.array([15.0, 15.1 ]), # nearly identical magnitudes
xe = np.array([ 0.002, 0.002 ]),
ye = np.array([ 0.002, 0.002 ]),
me = np.array([ 0.05, 0.05 ]),
t = np.array([2010.0, 2010.0 ]),
)
sl2 = starlists.StarList.from_dict(
name = np.array(['S0-9', 'S0-10' ]),
x = np.array([ 0.001, 0.029 ]),
y = np.array([ 0.001, 0.009 ]),
m = np.array([15.0, 15.1 ]),
xe = np.array([ 0.002, 0.002 ]),
ye = np.array([ 0.002, 0.002 ]),
me = np.array([ 0.05, 0.05 ]),
t = np.array([2022.0, 2022.0 ]),
)
al_tb = align.AlignmentFitCombine(
star_lists=[sl2],
ref_list=ref2,
n_iterations=1,
dr_tol=0.05,
dm_tol=0.5,
name_match='tiebreak',
name_match_dr_tol=0.05,
verbose=0,
)
al_tb.fit()
print('Stars after tiebreak alignment:', len(al_tb.ref_table))
print(' →', list(al_tb.ref_table['name']))
4. Excluding anonymous-prefix names¶
Not every name in a starlist is meaningful. FlyStar itself adds a numeric
prefix (e.g. ' 3_S0-2') to new stars it discovers during alignment; those
entries should not drive name matching. The name_match_exclude argument
accepts a tuple of string prefixes; any name beginning with one of them is
treated as anonymous and is matched by position only.
The default is ('star',), which masks names like 'star_001' that generic
source-extraction tools often produce. Override it if your pipeline uses a
different convention:
# Exclude names starting with 'src' as well as the default 'star'.
al_excl = align.AlignmentFitCombine(
star_lists=[sl],
ref_list=ref,
n_iterations=1,
dr_tol=0.05,
dm_tol=1.0,
name_match='override',
name_match_dr_tol=0.20,
name_match_exclude=('star', 'src'), # custom prefixes
verbose=0,
)
al_excl.fit()
print('Done. Stars in reference:', len(al_excl.ref_table))
5. Summary: which mode to choose¶
Scenario |
Recommended mode |
|---|---|
Fast-moving star that leaves |
|
Reliable external catalogue identifications to preserve |
|
Crowded field: stars within |
|
No named identifications, or names are unreliable |
|
Choosing name_match_dr_tol¶
Set name_match_dr_tol to roughly the largest displacement you expect for a
named star between adjacent epochs. A value too small will fail to link the
star; a value too large risks linking two different stars that happen to share
a name after a catalogue cross-match error. For Galactic Centre work with
typical proper motions of a few mas/yr and epoch gaps of 1–2 years, values of
50–200 mas are common.