rms-starcat 0.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of rms-starcat might be problematic. Click here for more details.
- rms_starcat-0.0.1.dist-info/LICENSE +201 -0
- rms_starcat-0.0.1.dist-info/METADATA +39 -0
- rms_starcat-0.0.1.dist-info/RECORD +11 -0
- rms_starcat-0.0.1.dist-info/WHEEL +5 -0
- rms_starcat-0.0.1.dist-info/top_level.txt +1 -0
- starcat/__init__.py +12 -0
- starcat/_version.py +16 -0
- starcat/spice.py +97 -0
- starcat/starcatalog.py +414 -0
- starcat/ucac4.py +1075 -0
- starcat/ybsc.py +510 -0
starcat/starcatalog.py
ADDED
|
@@ -0,0 +1,414 @@
|
|
|
1
|
+
################################################################################
|
|
2
|
+
# starcat/starcatalog.py
|
|
3
|
+
################################################################################
|
|
4
|
+
|
|
5
|
+
import inspect
|
|
6
|
+
import numpy as np
|
|
7
|
+
|
|
8
|
+
DPR = 180/np.pi
|
|
9
|
+
RPD = np.pi/180
|
|
10
|
+
AS_TO_DEG = 1/3600.
|
|
11
|
+
AS_TO_RAD = AS_TO_DEG * RPD
|
|
12
|
+
MAS_TO_DEG = AS_TO_DEG / 1000.
|
|
13
|
+
MAS_TO_RAD = MAS_TO_DEG * RPD
|
|
14
|
+
YEAR_TO_SEC = 1/365.25/86400.
|
|
15
|
+
|
|
16
|
+
TWOPI = 2*np.pi
|
|
17
|
+
HALFPI = np.pi/2
|
|
18
|
+
|
|
19
|
+
#===============================================================================
|
|
20
|
+
#
|
|
21
|
+
# Jacobson B-V photometry vs. stellar spectral classification
|
|
22
|
+
#
|
|
23
|
+
# Data from
|
|
24
|
+
# Zombeck, M. V. Handbook of Space Astronomy and Astrophysics,
|
|
25
|
+
# Cambridge, UK: Cambridge University Press, 2nd ed., pp. 68-70
|
|
26
|
+
# http://ads.harvard.edu/books/hsaa/
|
|
27
|
+
# as transcribed:
|
|
28
|
+
# http://www.vendian.org/mncharity/dir3/starcolor/details.html
|
|
29
|
+
#
|
|
30
|
+
# The tables below only include main sequence stars, but the temperature
|
|
31
|
+
# difference between main sequence stars and giant stars is minimal for our
|
|
32
|
+
# purposes. Missing values have been linearly interpolated.
|
|
33
|
+
#
|
|
34
|
+
#===============================================================================
|
|
35
|
+
|
|
36
|
+
SCLASS_TO_B_MINUS_V = {
|
|
37
|
+
'O5': -0.32,
|
|
38
|
+
'O6': -0.32,
|
|
39
|
+
'O7': -0.32,
|
|
40
|
+
'O8': -0.31,
|
|
41
|
+
'O9': -0.31,
|
|
42
|
+
'O9.5': -0.30,
|
|
43
|
+
'B0': -0.30,
|
|
44
|
+
'B0.5': -0.28,
|
|
45
|
+
'B1': -0.26,
|
|
46
|
+
'B2': -0.24,
|
|
47
|
+
'B3': -0.20,
|
|
48
|
+
'B4': -0.18, # Interp
|
|
49
|
+
'B5': -0.16,
|
|
50
|
+
'B6': -0.14,
|
|
51
|
+
'B7': -0.12,
|
|
52
|
+
'B8': -0.09,
|
|
53
|
+
'B9': -0.06,
|
|
54
|
+
'A0': +0.00,
|
|
55
|
+
'A1': +0.02, # Interp
|
|
56
|
+
'A2': +0.04, # Interp
|
|
57
|
+
'A3': +0.06,
|
|
58
|
+
'A4': +0.10, # Interp
|
|
59
|
+
'A5': +0.14,
|
|
60
|
+
'A6': +0.16, # Interp
|
|
61
|
+
'A7': +0.19,
|
|
62
|
+
'A8': +0.23, # Interp
|
|
63
|
+
'A9': +0.27, # Interp
|
|
64
|
+
'F0': +0.31,
|
|
65
|
+
'F1': +0.33, # Interp
|
|
66
|
+
'F2': +0.36,
|
|
67
|
+
'F3': +0.38, # Interp
|
|
68
|
+
'F4': +0.41, # Interp
|
|
69
|
+
'F5': +0.43,
|
|
70
|
+
'F6': +0.47, # Interp
|
|
71
|
+
'F7': +0.51, # Interp
|
|
72
|
+
'F8': +0.54,
|
|
73
|
+
'F9': +0.56, # Interp
|
|
74
|
+
'G0': +0.59,
|
|
75
|
+
'G1': +0.61, # Interp
|
|
76
|
+
'G2': +0.63,
|
|
77
|
+
'G3': +0.64, # Interp
|
|
78
|
+
'G4': +0.65, # Interp
|
|
79
|
+
'G5': +0.66,
|
|
80
|
+
'G6': +0.69, # Interp
|
|
81
|
+
'G7': +0.72, # Interp
|
|
82
|
+
'G8': +0.74,
|
|
83
|
+
'G9': +0.78, # Interp
|
|
84
|
+
'K0': +0.82,
|
|
85
|
+
'K1': +0.87, # Interp
|
|
86
|
+
'K2': +0.92,
|
|
87
|
+
'K3': +0.99, # Interp
|
|
88
|
+
'K4': +1.07, # Interp
|
|
89
|
+
'K5': +1.15,
|
|
90
|
+
'K6': +1.22, # Interp
|
|
91
|
+
'K7': +1.30,
|
|
92
|
+
'K8': +1.33, # Interp
|
|
93
|
+
'K9': +1.37, # Interp
|
|
94
|
+
'M0': +1.41,
|
|
95
|
+
'M1': +1.48,
|
|
96
|
+
'M2': +1.52,
|
|
97
|
+
'M3': +1.55,
|
|
98
|
+
'M4': +1.56,
|
|
99
|
+
'M5': +1.61,
|
|
100
|
+
'M6': +1.72,
|
|
101
|
+
'M7': +1.84,
|
|
102
|
+
'M8': +2.00
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
SCLASS_TO_SURFACE_TEMP = {
|
|
106
|
+
'O5': 38000,
|
|
107
|
+
'O6': 38000,
|
|
108
|
+
'O7': 38000,
|
|
109
|
+
'O8': 35000,
|
|
110
|
+
'O9': 35000,
|
|
111
|
+
'O9.5': 31900,
|
|
112
|
+
'B0': 30000,
|
|
113
|
+
'B0.5': 27000,
|
|
114
|
+
'B1': 24200,
|
|
115
|
+
'B2': 22100,
|
|
116
|
+
'B3': 18800,
|
|
117
|
+
'B4': 17600, # Interp
|
|
118
|
+
'B5': 16400,
|
|
119
|
+
'B6': 15400,
|
|
120
|
+
'B7': 14500,
|
|
121
|
+
'B8': 13400,
|
|
122
|
+
'B9': 12400,
|
|
123
|
+
'A0': 10800,
|
|
124
|
+
'A1': 10443, # Interp
|
|
125
|
+
'A2': 10086, # Interp
|
|
126
|
+
'A3': 9730,
|
|
127
|
+
'A4': 9175, # Interp
|
|
128
|
+
'A5': 8620,
|
|
129
|
+
'A6': 8405, # Interp
|
|
130
|
+
'A7': 8190,
|
|
131
|
+
'A8': 7873, # Interp
|
|
132
|
+
'A9': 7557, # Interp
|
|
133
|
+
'F0': 7240,
|
|
134
|
+
'F1': 7085, # Interp
|
|
135
|
+
'F2': 6930,
|
|
136
|
+
'F3': 6800, # Interp
|
|
137
|
+
'F4': 6670, # Interp
|
|
138
|
+
'F5': 6540,
|
|
139
|
+
'F6': 6427, # Interp
|
|
140
|
+
'F7': 6313, # Interp
|
|
141
|
+
'F8': 6200,
|
|
142
|
+
'F9': 6060, # Interp
|
|
143
|
+
'G0': 5920,
|
|
144
|
+
'G1': 5850, # Interp
|
|
145
|
+
'G2': 5780,
|
|
146
|
+
'G3': 5723, # Interp
|
|
147
|
+
'G4': 5667, # Interp
|
|
148
|
+
'G5': 5610,
|
|
149
|
+
'G6': 5570, # Interp
|
|
150
|
+
'G7': 5530, # Interp
|
|
151
|
+
'G8': 5490,
|
|
152
|
+
'G9': 5365, # Interp
|
|
153
|
+
'K0': 5240,
|
|
154
|
+
'K1': 5010, # Interp
|
|
155
|
+
'K2': 4780,
|
|
156
|
+
'K3': 4706, # Interp
|
|
157
|
+
'K4': 4632, # Interp
|
|
158
|
+
'K5': 4558, # Interp
|
|
159
|
+
'K6': 4484, # Interp
|
|
160
|
+
'K7': 4410,
|
|
161
|
+
'K8': 4247, # Interp
|
|
162
|
+
'K9': 4083, # Interp
|
|
163
|
+
'M0': 3800, # M class from https://arxiv.org/abs/0903.3371
|
|
164
|
+
'M1': 3600,
|
|
165
|
+
'M2': 3400,
|
|
166
|
+
'M3': 3250,
|
|
167
|
+
'M4': 3100,
|
|
168
|
+
'M5': 2800,
|
|
169
|
+
'M6': 2600,
|
|
170
|
+
'M7': 2500,
|
|
171
|
+
'M8': 2300,
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
#===============================================================================
|
|
176
|
+
#
|
|
177
|
+
# STAR Superclass
|
|
178
|
+
#
|
|
179
|
+
#===============================================================================
|
|
180
|
+
|
|
181
|
+
class Star(object):
|
|
182
|
+
"""A holder for star attributes.
|
|
183
|
+
|
|
184
|
+
This is the base class that defines attributes common to all
|
|
185
|
+
star catalogs."""
|
|
186
|
+
|
|
187
|
+
def __init__(self):
|
|
188
|
+
"""Constructor for Star superclass."""
|
|
189
|
+
|
|
190
|
+
self.ra = None
|
|
191
|
+
"""Right ascension at J2000 epoch (radians)"""
|
|
192
|
+
|
|
193
|
+
self.ra_sigma = None
|
|
194
|
+
"""Right ascension error (radians)"""
|
|
195
|
+
|
|
196
|
+
self.dec = None
|
|
197
|
+
"""Declination at J2000 epoch (radians)"""
|
|
198
|
+
|
|
199
|
+
self.dec_sigma = None
|
|
200
|
+
"""Declination error (radians)"""
|
|
201
|
+
|
|
202
|
+
self.vmag = None
|
|
203
|
+
"""Visual magnitude"""
|
|
204
|
+
|
|
205
|
+
self.vmag_sigma = None
|
|
206
|
+
"""Visual magnitude error"""
|
|
207
|
+
|
|
208
|
+
self.pm_ra = None
|
|
209
|
+
"""Proper motion in RA (radians/sec)"""
|
|
210
|
+
|
|
211
|
+
self.pm_ra_sigma = None
|
|
212
|
+
"""Proper motion in RA error (radians/sec)"""
|
|
213
|
+
|
|
214
|
+
self.pm_dec = None
|
|
215
|
+
"""Proper motion in DEC (radians/sec)"""
|
|
216
|
+
|
|
217
|
+
self.pm_dec_sigma = None
|
|
218
|
+
"""Proper motion in DEC error (radians/sec)"""
|
|
219
|
+
|
|
220
|
+
self.unique_number = None
|
|
221
|
+
"""Unique catalog number"""
|
|
222
|
+
|
|
223
|
+
def __str__(self):
|
|
224
|
+
ret = 'UNIQUE ID %d' % (self.unique_number)
|
|
225
|
+
|
|
226
|
+
if self.ra is not None:
|
|
227
|
+
ret += ' | RA %.7f' % (self.ra)
|
|
228
|
+
if self.ra_sigma is not None:
|
|
229
|
+
ret += ' [+/- %.7f]' % (self.ra_sigma)
|
|
230
|
+
|
|
231
|
+
ra_deg = self.ra*DPR/15 # In hours
|
|
232
|
+
hh = int(ra_deg)
|
|
233
|
+
mm = int((ra_deg-hh)*60)
|
|
234
|
+
ss = (ra_deg-hh-mm/60.)*3600
|
|
235
|
+
ret += ' (%02dh%02dm%05.3fs' % (hh,mm,ss)
|
|
236
|
+
if self.ra_sigma is not None:
|
|
237
|
+
ret += ' +/- %.4fs' % (self.ra_sigma*DPR*3600)
|
|
238
|
+
ret += ')'
|
|
239
|
+
|
|
240
|
+
if self.dec is not None:
|
|
241
|
+
ret += ' | DEC %.7f' % (self.dec)
|
|
242
|
+
if self.dec_sigma is not None:
|
|
243
|
+
ret += ' [+/- %.7f]' % (self.dec_sigma)
|
|
244
|
+
|
|
245
|
+
dec_deg = self.dec*DPR # In degrees
|
|
246
|
+
neg = '+'
|
|
247
|
+
if dec_deg < 0.:
|
|
248
|
+
neg = '-'
|
|
249
|
+
dec_deg = -dec_deg
|
|
250
|
+
dd = int(dec_deg)
|
|
251
|
+
mm = int((dec_deg-dd)*60)
|
|
252
|
+
ss = (dec_deg-dd-mm/60.)*3600
|
|
253
|
+
ret += ' (%s%03dd%02dm%05.3fs' % (neg,dd,mm,ss)
|
|
254
|
+
|
|
255
|
+
if self.dec_sigma is not None:
|
|
256
|
+
ret += ' +/- %.4fs' % (self.dec_sigma*DPR*3600)
|
|
257
|
+
ret += ')'
|
|
258
|
+
|
|
259
|
+
ret += '\n'
|
|
260
|
+
|
|
261
|
+
if self.vmag is not None:
|
|
262
|
+
ret += 'VMAG %6.3f ' % (self.vmag)
|
|
263
|
+
if self.vmag_sigma is not None:
|
|
264
|
+
ret += '+/- %6.3f ' % (self.vmag_sigma)
|
|
265
|
+
|
|
266
|
+
if self.pm_ra is not None:
|
|
267
|
+
ret += ' | PM RA %.3f mas/yr ' % (self.pm_ra/MAS_TO_RAD/YEAR_TO_SEC)
|
|
268
|
+
if self.pm_ra_sigma:
|
|
269
|
+
ret += '+/- %.3f ' % (self.pm_ra_sigma/MAS_TO_RAD/YEAR_TO_SEC)
|
|
270
|
+
|
|
271
|
+
if self.pm_dec is not None:
|
|
272
|
+
ret += ' | PM DEC %.3f mas/yr ' % (self.pm_dec/MAS_TO_RAD/YEAR_TO_SEC)
|
|
273
|
+
if self.pm_dec_sigma:
|
|
274
|
+
ret += '+/- %.3f ' % (self.pm_dec_sigma/MAS_TO_RAD/YEAR_TO_SEC)
|
|
275
|
+
|
|
276
|
+
ret += '\n'
|
|
277
|
+
|
|
278
|
+
return ret
|
|
279
|
+
|
|
280
|
+
def to_dict(self):
|
|
281
|
+
attribs = inspect.getmembers(self, lambda a:not(inspect.isroutine(a)))
|
|
282
|
+
attribs = [a for a in attribs if not(a[0].startswith('__') and a[0].endswith('__'))]
|
|
283
|
+
return dict(attribs)
|
|
284
|
+
|
|
285
|
+
def from_dict(self, d):
|
|
286
|
+
for key in list(d.keys()):
|
|
287
|
+
setattr(self, key, d[key])
|
|
288
|
+
|
|
289
|
+
def ra_dec_with_pm(self, tdb):
|
|
290
|
+
"""Return the star's RA and DEC adjusted for proper motion.
|
|
291
|
+
|
|
292
|
+
If no proper motion is available, the original RA and DEC are returned.
|
|
293
|
+
|
|
294
|
+
Input:
|
|
295
|
+
tdb time since the J2000 epoch in seconds
|
|
296
|
+
"""
|
|
297
|
+
|
|
298
|
+
if self.pm_ra is None or self.pm_dec is None:
|
|
299
|
+
return (self.ra, self.dec)
|
|
300
|
+
|
|
301
|
+
return (self.ra + tdb*self.pm_ra, self.dec + tdb*self.pm_dec)
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
class StarCatalog(object):
|
|
305
|
+
def __init__(self):
|
|
306
|
+
pass
|
|
307
|
+
|
|
308
|
+
def count_stars(self, **kwargs):
|
|
309
|
+
"""Count the stars that match the given search criteria."""
|
|
310
|
+
count = 0
|
|
311
|
+
for result in self.find_stars(full_result=False, **kwargs):
|
|
312
|
+
count += 1
|
|
313
|
+
return count
|
|
314
|
+
|
|
315
|
+
def find_stars(self, **kwargs):
|
|
316
|
+
"""Find the stars that match the given search criteria.
|
|
317
|
+
|
|
318
|
+
Optional arguments: DEFAULT
|
|
319
|
+
ra_min, ra_max 0, 2PI RA range in radians
|
|
320
|
+
dec_min, dec_max -PI, PI DEC range in radians
|
|
321
|
+
vmag_min, vmag_max ALL Magnitude range
|
|
322
|
+
"""
|
|
323
|
+
|
|
324
|
+
kwargs = kwargs.copy() # Private copy so pop doesn't mutate
|
|
325
|
+
ra_min = np.clip(kwargs.pop('ra_min', 0), 0., TWOPI)
|
|
326
|
+
ra_max = np.clip(kwargs.pop('ra_max', TWOPI), 0., TWOPI)
|
|
327
|
+
dec_min = np.clip(kwargs.pop('dec_min', -HALFPI), -HALFPI, HALFPI)
|
|
328
|
+
dec_max = np.clip(kwargs.pop('dec_max', HALFPI), -HALFPI, HALFPI)
|
|
329
|
+
|
|
330
|
+
if ra_min > ra_max:
|
|
331
|
+
if dec_min > dec_max:
|
|
332
|
+
# Split into four searches
|
|
333
|
+
for star in self._find_stars(0., ra_max, -HALFPI, dec_max,
|
|
334
|
+
**kwargs):
|
|
335
|
+
yield star
|
|
336
|
+
for star in self._find_stars(ra_min, TWOPI, -HALFPI, dec_max,
|
|
337
|
+
**kwargs):
|
|
338
|
+
yield star
|
|
339
|
+
for star in self._find_stars(0., ra_max, dec_min, HALFPI,
|
|
340
|
+
**kwargs):
|
|
341
|
+
yield star
|
|
342
|
+
for star in self._find_stars(ra_min, TWOPI, dec_min, HALFPI,
|
|
343
|
+
**kwargs):
|
|
344
|
+
yield star
|
|
345
|
+
else:
|
|
346
|
+
# Split into two searches - RA
|
|
347
|
+
for star in self._find_stars(0., ra_max, dec_min, dec_max,
|
|
348
|
+
**kwargs):
|
|
349
|
+
yield star
|
|
350
|
+
for star in self._find_stars(ra_min, TWOPI, dec_min, dec_max,
|
|
351
|
+
**kwargs):
|
|
352
|
+
yield star
|
|
353
|
+
else:
|
|
354
|
+
if dec_min > dec_max:
|
|
355
|
+
# Split into two searches - DEC
|
|
356
|
+
for star in self._find_stars(ra_min, ra_max, -HALFPI, dec_max,
|
|
357
|
+
**kwargs):
|
|
358
|
+
yield star
|
|
359
|
+
for star in self._find_stars(ra_min, ra_max, dec_min, HALFPI,
|
|
360
|
+
**kwargs):
|
|
361
|
+
yield star
|
|
362
|
+
else:
|
|
363
|
+
# No need to split at all
|
|
364
|
+
for star in self._find_stars(ra_min, ra_max,
|
|
365
|
+
dec_min, dec_max, **kwargs):
|
|
366
|
+
yield star
|
|
367
|
+
|
|
368
|
+
def _find_stars(self, **kwargs):
|
|
369
|
+
assert False
|
|
370
|
+
|
|
371
|
+
@staticmethod
|
|
372
|
+
def sclass_from_bv(b, v):
|
|
373
|
+
"""Return a star's spectral class given photometric B and V."""
|
|
374
|
+
bmv = b-v
|
|
375
|
+
|
|
376
|
+
best_temp = None
|
|
377
|
+
best_sclass = None
|
|
378
|
+
best_resid = 1e38
|
|
379
|
+
|
|
380
|
+
min_bmv = 1e38
|
|
381
|
+
max_bmv = -1e38
|
|
382
|
+
for sclass, sbmv in SCLASS_TO_B_MINUS_V.items():
|
|
383
|
+
min_bmv = min(min_bmv, sbmv)
|
|
384
|
+
max_bmv = max(max_bmv, sbmv)
|
|
385
|
+
resid = abs(sbmv-bmv)
|
|
386
|
+
if resid < best_resid:
|
|
387
|
+
best_resid = resid
|
|
388
|
+
best_sclass = sclass
|
|
389
|
+
|
|
390
|
+
if min_bmv <= bmv <= max_bmv:
|
|
391
|
+
return best_sclass
|
|
392
|
+
|
|
393
|
+
return None
|
|
394
|
+
|
|
395
|
+
@staticmethod
|
|
396
|
+
def temperature_from_sclass(sclass):
|
|
397
|
+
"""Return a star's temperature (K) given its spectral class."""
|
|
398
|
+
if sclass[-1] == '*': # This happens on some SPICE catalog stars
|
|
399
|
+
sclass = sclass[:-1]
|
|
400
|
+
sclass = sclass.strip().upper()
|
|
401
|
+
try:
|
|
402
|
+
return SCLASS_TO_SURFACE_TEMP[sclass]
|
|
403
|
+
except KeyError:
|
|
404
|
+
return None
|
|
405
|
+
|
|
406
|
+
@staticmethod
|
|
407
|
+
def bmv_from_sclass(sclass):
|
|
408
|
+
if sclass[-1] == '*': # This happens on some SPICE catalog stars
|
|
409
|
+
sclass = sclass[:-1]
|
|
410
|
+
sclass = sclass.strip().upper()
|
|
411
|
+
try:
|
|
412
|
+
return SCLASS_TO_B_MINUS_V[sclass]
|
|
413
|
+
except KeyError:
|
|
414
|
+
return None
|