stdpipe 0.2.3__tar.gz → 0.2.4__tar.gz

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.
Files changed (38) hide show
  1. {stdpipe-0.2.3 → stdpipe-0.2.4}/MANIFEST.in +1 -0
  2. stdpipe-0.2.4/PKG-INFO +29 -0
  3. {stdpipe-0.2.3 → stdpipe-0.2.4}/setup.py +1 -1
  4. stdpipe-0.2.4/stdpipe/flycheck_catalogs.py +637 -0
  5. {stdpipe-0.2.3 → stdpipe-0.2.4}/stdpipe/subtraction.py +2 -2
  6. {stdpipe-0.2.3 → stdpipe-0.2.4}/stdpipe/utils.py +13 -0
  7. stdpipe-0.2.4/stdpipe.egg-info/PKG-INFO +29 -0
  8. {stdpipe-0.2.3 → stdpipe-0.2.4}/stdpipe.egg-info/SOURCES.txt +4 -6
  9. stdpipe-0.2.4/tests/test_dependencies.py +1 -0
  10. stdpipe-0.2.3/PKG-INFO +0 -8
  11. stdpipe-0.2.3/notebooks/simple_lowlevel_pipeline.ipynb +0 -646
  12. stdpipe-0.2.3/notebooks/simple_pipeline.ipynb +0 -520
  13. stdpipe-0.2.3/notebooks/simulated_stars.ipynb +0 -568
  14. stdpipe-0.2.3/stdpipe/dpipeline.py +0 -0
  15. stdpipe-0.2.3/stdpipe.egg-info/PKG-INFO +0 -8
  16. {stdpipe-0.2.3 → stdpipe-0.2.4}/.gitignore +0 -0
  17. {stdpipe-0.2.3 → stdpipe-0.2.4}/LICENSE.md +0 -0
  18. {stdpipe-0.2.3 → stdpipe-0.2.4}/README.md +0 -0
  19. {stdpipe-0.2.3 → stdpipe-0.2.4}/setup.cfg +0 -0
  20. {stdpipe-0.2.3 → stdpipe-0.2.4}/stdpipe/__init__.py +0 -0
  21. {stdpipe-0.2.3 → stdpipe-0.2.4}/stdpipe/astrometry.py +0 -0
  22. {stdpipe-0.2.3 → stdpipe-0.2.4}/stdpipe/catalogs.py +0 -0
  23. {stdpipe-0.2.3 → stdpipe-0.2.4}/stdpipe/cutouts.py +0 -0
  24. {stdpipe-0.2.3 → stdpipe-0.2.4}/stdpipe/data/legacysurvey.py +0 -0
  25. {stdpipe-0.2.3 → stdpipe-0.2.4}/stdpipe/data/legacysurvey_bricks.fits.gz +0 -0
  26. {stdpipe-0.2.3 → stdpipe-0.2.4}/stdpipe/data/ps1skycells.txt +0 -0
  27. {stdpipe-0.2.3 → stdpipe-0.2.4/stdpipe}/data/ps1skycells.txt.orig +0 -0
  28. {stdpipe-0.2.3 → stdpipe-0.2.4}/stdpipe/db.py +0 -0
  29. {stdpipe-0.2.3 → stdpipe-0.2.4}/stdpipe/lcs.py +0 -0
  30. {stdpipe-0.2.3 → stdpipe-0.2.4}/stdpipe/photometry.py +0 -0
  31. {stdpipe-0.2.3 → stdpipe-0.2.4}/stdpipe/pipeline.py +0 -0
  32. {stdpipe-0.2.3 → stdpipe-0.2.4}/stdpipe/plots.py +0 -0
  33. {stdpipe-0.2.3 → stdpipe-0.2.4}/stdpipe/psf.py +0 -0
  34. {stdpipe-0.2.3 → stdpipe-0.2.4}/stdpipe/resolve.py +0 -0
  35. {stdpipe-0.2.3 → stdpipe-0.2.4}/stdpipe/templates.py +0 -0
  36. {stdpipe-0.2.3 → stdpipe-0.2.4}/stdpipe.egg-info/dependency_links.txt +0 -0
  37. {stdpipe-0.2.3 → stdpipe-0.2.4}/stdpipe.egg-info/requires.txt +0 -0
  38. {stdpipe-0.2.3 → stdpipe-0.2.4}/stdpipe.egg-info/top_level.txt +0 -0
@@ -1,2 +1,3 @@
1
1
  include README.md
2
2
  recursive-include data *
3
+ recursive-exclude notebooks *
stdpipe-0.2.4/PKG-INFO ADDED
@@ -0,0 +1,29 @@
1
+ Metadata-Version: 2.4
2
+ Name: stdpipe
3
+ Version: 0.2.4
4
+ Summary: Simple Transient Detection Pipeline
5
+ Home-page: https://github.com/karpov-sv/stdpipe
6
+ Author: Sergey Karpov
7
+ Author-email: karpov.sv@gmail.com
8
+ License-File: LICENSE.md
9
+ Requires-Dist: numpy
10
+ Requires-Dist: scipy
11
+ Requires-Dist: astropy>=4.1
12
+ Requires-Dist: matplotlib
13
+ Requires-Dist: astroquery>0.4.1
14
+ Requires-Dist: sep
15
+ Requires-Dist: astroscrappy
16
+ Requires-Dist: photutils
17
+ Requires-Dist: statsmodels
18
+ Requires-Dist: tqdm
19
+ Requires-Dist: regions
20
+ Requires-Dist: python-dateutil
21
+ Requires-Dist: requests
22
+ Requires-Dist: sip_tpv
23
+ Requires-Dist: pyfftw
24
+ Dynamic: author
25
+ Dynamic: author-email
26
+ Dynamic: home-page
27
+ Dynamic: license-file
28
+ Dynamic: requires-dist
29
+ Dynamic: summary
@@ -3,7 +3,7 @@
3
3
 
4
4
  from setuptools import setup, find_packages
5
5
 
6
- __version__ = '0.2.3'
6
+ __version__ = '0.2.4'
7
7
 
8
8
  requirements = [
9
9
  'numpy',
@@ -0,0 +1,637 @@
1
+ """
2
+ Module containing the routines for handling various online catalogues.
3
+ """
4
+
5
+ from __future__ import absolute_import, division, print_function, unicode_literals
6
+
7
+ import os, posixpath, shutil, tempfile
8
+ import numpy as np
9
+
10
+ from astropy.table import Table, MaskedColumn, vstack
11
+ from astroquery.vizier import Vizier
12
+ from astroquery.xmatch import XMatch
13
+ from astroquery.imcce import Skybot
14
+ from astroquery.ned import Ned
15
+ from astropy.coordinates import SkyCoord
16
+ from astropy import units as u
17
+ from astropy.time import Time
18
+
19
+ from . import astrometry
20
+
21
+ catalogs = {
22
+ 'ps1': {'vizier': 'II/349/ps1', 'name': 'PanSTARRS DR1'},
23
+ 'gaiadr2': {'vizier': 'I/345/gaia2', 'name': 'Gaia DR2', 'extra': ['E(BR/RP)']},
24
+ 'gaiaedr3': {'vizier': 'I/350/gaiaedr3', 'name': 'Gaia EDR3'},
25
+ 'gaiadr3syn': {
26
+ 'vizier': 'I/360/syntphot',
27
+ 'name': 'Gaia DR3 synthetic photometry',
28
+ 'extra': ['**', '_RAJ2000', '_DEJ2000'],
29
+ },
30
+ 'usnob1': {'vizier': 'I/284/out', 'name': 'USNO-B1'},
31
+ 'gsc': {'vizier': 'I/271/out', 'name': 'GSC 2.2'},
32
+ 'skymapper': {
33
+ 'vizier': 'II/358/smss',
34
+ 'name': 'SkyMapper DR1.1',
35
+ 'extra': [
36
+ '_RAJ2000',
37
+ '_DEJ2000',
38
+ 'e_uPSF',
39
+ 'e_vPSF',
40
+ 'e_gPSF',
41
+ 'e_rPSF',
42
+ 'e_iPSF',
43
+ 'e_zPSF',
44
+ ],
45
+ },
46
+ 'vsx': {'vizier': 'B/vsx/vsx', 'name': 'AAVSO VSX'},
47
+ 'apass': {'vizier': 'II/336/apass9', 'name': 'APASS DR9'},
48
+ 'sdss': {
49
+ 'vizier': 'V/147/sdss12',
50
+ 'name': 'SDSS DR12',
51
+ 'extra': ['_RAJ2000', '_DEJ2000'],
52
+ },
53
+ 'atlas': {
54
+ 'vizier': 'J/ApJ/867/105/refcat2',
55
+ 'name': 'ATLAS-REFCAT2',
56
+ 'extra': [
57
+ '_RAJ2000',
58
+ '_DEJ2000',
59
+ 'e_Gmag',
60
+ 'e_gmag',
61
+ 'e_rmag',
62
+ 'e_imag',
63
+ 'e_zmag',
64
+ 'e_Jmag',
65
+ 'e_Kmag',
66
+ ],
67
+ },
68
+ }
69
+
70
+
71
+ def get_cat_vizier(
72
+ ra0, dec0, sr0, catalog='ps1', limit=-1, filters={}, extra=[], get_distance=False, verbose=False
73
+ ):
74
+ """Download any catalogue from Vizier.
75
+
76
+ The catalogue may be anything recognizable by Vizier. For some most popular ones, we have additional support - we try to augment them with photometric measurements not originally present there, based on some analytical magnitude conversion formulae. These catalogues are:
77
+
78
+ - ps1 - Pan-STARRS DR1. We augment it with Johnson-Cousins B, V, R and I magnitudes
79
+ - gaiadr2 - Gaia DR2. We augment it with Johnson-Cousins B, V, R and I magnitudes, as well as Pan-STARRS and SDSS ones
80
+ - gaiaedr3 - Gaia eDR3
81
+ - gaiadr3syn - Gaia DR3 synthetic photometry based on XP spectra
82
+ - skymapper - SkyMapper DR1.1
83
+ - vsx - AAVSO Variable Stars Index
84
+ - apass - AAVSO APASS DR9
85
+ - sdss - SDSS DR12
86
+ - atlas - ATLAS-RefCat2, compilative all-sky reference catalogue with uniform zero-points in Pan-STARRS-like bands. We augment it with Johnson-Cousins B, V, R and I magnitudes the same way as Pan-STARRS.
87
+ - usnob1 - USNO-B1
88
+ - gsc - Guide Star Catalogue 2.2
89
+
90
+ :param ra0: Right Ascension of the field center, degrees
91
+ :param dec0: Declination of the field center, degrees
92
+ :param sr0: Field radius, degrees
93
+ :param catalog: Any Vizier catalogue identifier, or a catalogue short name (see above)
94
+ :param limit: Limit for the number of returned rows, optional
95
+ :param filters: Dictionary with column filters to be applied on Vizier side. Dictionary key is the column name, value - filter expression as documented at https://vizier.u-strasbg.fr/vizier/vizHelp/cst.htx
96
+ :param extra: List of extra column names to return in addition to default ones.
97
+ :param get_distance: If set, the distance from the field center will be returned in `_r` column.
98
+ :param verbose: Whether to show verbose messages during the run of the function or not. May be either boolean, or a `print`-like function.
99
+ :returns: astropy.table.Table with catalogue as returned by Vizier, with some additional columns added for supported catalogues.
100
+ """
101
+
102
+ # Simple Wrapper around print for logging in verbose mode only
103
+ log = (
104
+ (verbose if callable(verbose) else print)
105
+ if verbose
106
+ else lambda *args, **kwargs: None
107
+ )
108
+
109
+ # TODO: add positional errors
110
+
111
+ if catalog in catalogs:
112
+ # For some catalogs we may have some additional information
113
+ vizier_id = catalogs.get(catalog).get('vizier')
114
+ name = catalogs.get(catalog).get('name')
115
+
116
+ columns = (
117
+ ['*', 'RAJ2000', 'DEJ2000', 'e_RAJ2000', 'e_DEJ2000']
118
+ + extra
119
+ + catalogs.get(catalog).get('extra', [])
120
+ )
121
+ else:
122
+ vizier_id = catalog
123
+ name = catalog
124
+ columns = ['*', 'RAJ2000', 'DEJ2000', 'e_RAJ2000', 'e_DEJ2000'] + extra
125
+
126
+ log('Requesting from VizieR:', vizier_id, 'columns:', columns)
127
+ log('Center: %.3f %.3f' % (ra0, dec0), 'radius: %.3f' % sr0)
128
+ log('Filters:', filters)
129
+
130
+ vizier = Vizier(row_limit=limit, columns=columns, column_filters=filters)
131
+ cats = vizier.query_region(
132
+ SkyCoord(ra0, dec0, unit='deg'), radius=sr0 * u.deg, catalog=vizier_id
133
+ )
134
+
135
+ if not cats or not len(cats) == 1:
136
+ cats = vizier.query_region(
137
+ SkyCoord(ra0, dec0, unit='deg'),
138
+ radius=sr0 * u.deg,
139
+ catalog=vizier_id,
140
+ cache=False,
141
+ )
142
+
143
+ if not cats or not len(cats) == 1:
144
+ log('Error requesting catalogue', catalog)
145
+ return None
146
+
147
+ cat = cats[0]
148
+ cat.meta['vizier_id'] = vizier_id
149
+ cat.meta['name'] = name
150
+
151
+ log('Got', len(cat), 'entries with', len(cat.colnames), 'columns')
152
+
153
+ # Fix _RAJ2000/_DEJ2000
154
+ if (
155
+ '_RAJ2000' in cat.keys()
156
+ and '_DEJ2000' in cat.keys()
157
+ and not 'RAJ2000' in cat.keys()
158
+ ):
159
+ cat.rename_columns(['_RAJ2000', '_DEJ2000'], ['RAJ2000', 'DEJ2000'])
160
+
161
+ if get_distance and 'RAJ2000' in cat.colnames and 'DEJ2000' in cat.colnames:
162
+ log("Augmenting the catalogue with distances from field center")
163
+ cat['_r'] = astrometry.spherical_distance(ra0, dec0, cat['RAJ2000'], cat['DEJ2000'])
164
+
165
+ # Augment catalogue with additional bandpasses
166
+
167
+ if catalog == 'ps1' or catalog == 'atlas':
168
+ # Alternative PS1 transfromation from https://arxiv.org/pdf/1706.06147.pdf, Stetson, seems better with Landolt than official one
169
+ # cat['B'] = cat['gmag'] + 0.199 + 0.540*(cat['gmag'] - cat['rmag']) + 0.016*(cat['gmag'] - cat['rmag'])**2
170
+ # cat['V'] = cat['gmag'] - 0.020 - 0.498*(cat['gmag'] - cat['rmag']) - 0.008*(cat['gmag'] - cat['rmag'])**2
171
+ # cat['R'] = cat['rmag'] - 0.163 - 0.086*(cat['gmag'] - cat['rmag']) - 0.061*(cat['gmag'] - cat['rmag'])**2
172
+ # cat['I'] = cat['imag'] - 0.387 - 0.123*(cat['gmag'] - cat['rmag']) - 0.034*(cat['gmag'] - cat['rmag'])**2
173
+
174
+ log("Augmenting the catalogue with Johnson-Cousins photometry")
175
+
176
+ # My own fit on Landolt+Stetson standards from https://arxiv.org/pdf/2205.06186.pdf
177
+ pB1, pB2 = (
178
+ [
179
+ 0.10339527794499666,
180
+ -0.492149523946056,
181
+ 1.2093816061394638,
182
+ 0.061925048331498395,
183
+ ],
184
+ [
185
+ -0.2571974580267897,
186
+ 0.9211495207523038,
187
+ -0.8243222108864755,
188
+ 0.0619250483314976,
189
+ ],
190
+ )
191
+ pV1, pV2 = (
192
+ [
193
+ -0.011452922062676726,
194
+ -9.949308251868327e-05,
195
+ -0.4650511584366353,
196
+ -0.007076854914511554,
197
+ ],
198
+ [
199
+ 0.012749150754020416,
200
+ 0.057554580469724864,
201
+ -0.09019328095355343,
202
+ -0.007076854914511329,
203
+ ],
204
+ )
205
+ pR1, pR2 = (
206
+ [
207
+ 0.004905242602502597,
208
+ -0.046545625824660514,
209
+ 0.07830702317352654,
210
+ -0.08438139204305026,
211
+ ],
212
+ [
213
+ -0.07782426914647306,
214
+ 0.14090289318728444,
215
+ -0.3634922073369279,
216
+ -0.08438139204305031,
217
+ ],
218
+ )
219
+ pI1, pI2 = (
220
+ [
221
+ -0.02274814414922734,
222
+ 0.048462952908062046,
223
+ -0.046965058282604985,
224
+ -0.19478935830847588,
225
+ ],
226
+ [
227
+ 0.025124060889537177,
228
+ -0.048672562735374666,
229
+ -1.199591061144479,
230
+ -0.1947893583084762,
231
+ ],
232
+ )
233
+
234
+ cat['Bmag'] = (
235
+ cat['gmag']
236
+ + np.polyval(pB1, cat['gmag'] - cat['rmag'])
237
+ + np.polyval(pB2, cat['rmag'] - cat['imag'])
238
+ )
239
+ cat['Vmag'] = (
240
+ cat['gmag']
241
+ + np.polyval(pV1, cat['gmag'] - cat['rmag'])
242
+ + np.polyval(pV2, cat['rmag'] - cat['imag'])
243
+ )
244
+ cat['Rmag'] = (
245
+ cat['rmag']
246
+ + np.polyval(pR1, cat['gmag'] - cat['rmag'])
247
+ + np.polyval(pR2, cat['rmag'] - cat['imag'])
248
+ )
249
+ cat['Imag'] = (
250
+ cat['imag']
251
+ + np.polyval(pI1, cat['gmag'] - cat['rmag'])
252
+ + np.polyval(pI2, cat['imag'] - cat['zmag'])
253
+ )
254
+
255
+ cat['e_Bmag'] = cat['e_gmag']
256
+ cat['e_Vmag'] = cat['e_gmag']
257
+ cat['e_Rmag'] = cat['e_rmag']
258
+ cat['e_Imag'] = cat['e_imag']
259
+
260
+ # Copies of columns for convenience
261
+ for _ in ['B', 'V', 'R', 'I']:
262
+ cat[_] = cat[_ + 'mag']
263
+
264
+ cat['good'] = (cat['gmag'] - cat['rmag'] > -0.5) & (
265
+ cat['gmag'] - cat['rmag'] < 2.5
266
+ )
267
+ cat['good'] &= (cat['rmag'] - cat['imag'] > -0.5) & (
268
+ cat['rmag'] - cat['imag'] < 2.0
269
+ )
270
+ cat['good'] &= (cat['imag'] - cat['zmag'] > -0.5) & (
271
+ cat['imag'] - cat['zmag'] < 1.0
272
+ )
273
+
274
+ # to SDSS, zero points and color terms from https://arxiv.org/pdf/1203.0297.pdf
275
+ cat['g_SDSS'] = (
276
+ cat['gmag']
277
+ + 0.013
278
+ + 0.145 * (cat['gmag'] - cat['rmag'])
279
+ + 0.019 * (cat['gmag'] - cat['rmag']) ** 2
280
+ )
281
+ cat['r_SDSS'] = (
282
+ cat['rmag']
283
+ - 0.001
284
+ + 0.004 * (cat['gmag'] - cat['rmag'])
285
+ + 0.007 * (cat['gmag'] - cat['rmag']) ** 2
286
+ )
287
+ cat['i_SDSS'] = (
288
+ cat['imag']
289
+ - 0.005
290
+ + 0.011 * (cat['gmag'] - cat['rmag'])
291
+ + 0.010 * (cat['gmag'] - cat['rmag']) ** 2
292
+ )
293
+ cat['z_SDSS'] = cat['zmag']
294
+
295
+ elif catalog == 'gaiadr2':
296
+ log("Augmenting the catalogue with Johnson-Cousins photometry")
297
+
298
+ # My simple Gaia DR2 to Johnson conversion based on Stetson standards
299
+ pB = [
300
+ -0.05927724559795761,
301
+ 0.4224326324292696,
302
+ 0.626219707920836,
303
+ -0.011211539139725953,
304
+ ]
305
+ pV = [
306
+ 0.0017624722901609662,
307
+ 0.15671377090187089,
308
+ 0.03123927839356175,
309
+ 0.041448557506784556,
310
+ ]
311
+ pR = [
312
+ 0.02045449129406191,
313
+ 0.054005149296716175,
314
+ -0.3135475489352255,
315
+ 0.020545083667168156,
316
+ ]
317
+ pI = [
318
+ 0.005092289380850884,
319
+ 0.07027022935721515,
320
+ -0.7025553064161775,
321
+ -0.02747532184796779,
322
+ ]
323
+
324
+ pCB = [876.4047401692277, 5.114021693079334, -2.7332873314449326, 0]
325
+ pCV = [98.03049528983964, 20.582521666713028, 0.8690079603974803, 0]
326
+ pCR = [347.42190542330945, 39.42482430363565, 0.8626828845232541, 0]
327
+ pCI = [79.4028706486939, 9.176899238787003, -0.7826315256072135, 0]
328
+
329
+ g = cat['Gmag']
330
+ bp_rp = cat['BPmag'] - cat['RPmag']
331
+
332
+ # phot_bp_rp_excess_factor == E(BR/RP) == E_BR_RP_
333
+ Cstar = cat['E_BR_RP_'] - np.polyval(
334
+ [-0.00445024, 0.0570293, -0.02810592, 1.20477819], bp_rp
335
+ )
336
+
337
+ # https://www.cosmos.esa.int/web/gaia/dr2-known-issues#PhotometrySystematicEffectsAndResponseCurves
338
+ gcorr = g.copy()
339
+ gcorr[(g > 2) & (g < 6)] = (
340
+ -0.047344
341
+ + 1.16405 * g[(g > 2) & (g < 6)]
342
+ - 0.046799 * g[(g > 2) & (g < 6)] ** 2
343
+ + 0.0035015 * g[(g > 2) & (g < 6)] ** 3
344
+ )
345
+ gcorr[(g > 6) & (g < 16)] = g[(g > 6) & (g < 16)] - 0.0032 * (
346
+ g[(g > 6) & (g < 16)] - 6
347
+ )
348
+ gcorr[g > 16] = g[g > 16] - 0.032
349
+ g = gcorr
350
+
351
+ cat['Bmag'] = g + np.polyval(pB, bp_rp) + np.polyval(pCB, Cstar)
352
+ cat['Vmag'] = g + np.polyval(pV, bp_rp) + np.polyval(pCV, Cstar)
353
+ cat['Rmag'] = g + np.polyval(pR, bp_rp) + np.polyval(pCR, Cstar)
354
+ cat['Imag'] = g + np.polyval(pI, bp_rp) + np.polyval(pCI, Cstar)
355
+
356
+ # Rough estimation of magnitude error, just from G band
357
+ for _ in ['B', 'V', 'R', 'I', 'g', 'r']:
358
+ cat['e_' + _ + 'mag'] = cat['e_Gmag']
359
+
360
+ # Copies of columns for convenience
361
+ for _ in ['B', 'V', 'R', 'I']:
362
+ cat[_] = cat[_ + 'mag']
363
+
364
+ # to PS1 - FIXME: there are some uncorrected color and magnitude trends!
365
+ cat['gmag'] = (
366
+ cat['Bmag']
367
+ - 0.108
368
+ - 0.485 * (cat['Bmag'] - cat['Vmag'])
369
+ - 0.032 * (cat['Bmag'] - cat['Vmag']) ** 2
370
+ )
371
+ cat['rmag'] = (
372
+ cat['Vmag']
373
+ + 0.082
374
+ - 0.462 * (cat['Bmag'] - cat['Vmag'])
375
+ + 0.041 * (cat['Bmag'] - cat['Vmag']) ** 2
376
+ )
377
+
378
+ # to SDSS, from https://gea.esac.esa.int/archive/documentation/GDR2/Data_processing/chap_cu5pho/sec_cu5pho_calibr/ssec_cu5pho_PhotTransf.html
379
+ cat['g_SDSS'] = g - (
380
+ 0.13518 - 0.46245 * bp_rp - 0.25171 * bp_rp ** 2 + 0.021349 * bp_rp ** 3
381
+ )
382
+ cat['r_SDSS'] = g - (
383
+ -0.12879 + 0.24662 * bp_rp - 0.027464 * bp_rp ** 2 - 0.049465 * bp_rp ** 3
384
+ )
385
+ cat['i_SDSS'] = g - (-0.29676 + 0.64728 * bp_rp - 0.10141 * bp_rp ** 2)
386
+
387
+ elif catalog == 'skymapper':
388
+ # to PS1
389
+ for _ in ['u', 'g', 'r', 'i', 'z']:
390
+ cat[_ + 'mag'] = cat[_ + 'PSF']
391
+ cat['e_' + _ + 'mag'] = cat['e_' + _ + 'PSF']
392
+
393
+ log("Augmenting the catalogue with Johnson-Cousins photometry")
394
+
395
+ # Johnson-Cousins, copy from PS1 above
396
+ pB1, pB2 = (
397
+ [
398
+ 0.10339527794499666,
399
+ -0.492149523946056,
400
+ 1.2093816061394638,
401
+ 0.061925048331498395,
402
+ ],
403
+ [
404
+ -0.2571974580267897,
405
+ 0.9211495207523038,
406
+ -0.8243222108864755,
407
+ 0.0619250483314976,
408
+ ],
409
+ )
410
+ pV1, pV2 = (
411
+ [
412
+ -0.011452922062676726,
413
+ -9.949308251868327e-05,
414
+ -0.4650511584366353,
415
+ -0.007076854914511554,
416
+ ],
417
+ [
418
+ 0.012749150754020416,
419
+ 0.057554580469724864,
420
+ -0.09019328095355343,
421
+ -0.007076854914511329,
422
+ ],
423
+ )
424
+ pR1, pR2 = (
425
+ [
426
+ 0.004905242602502597,
427
+ -0.046545625824660514,
428
+ 0.07830702317352654,
429
+ -0.08438139204305026,
430
+ ],
431
+ [
432
+ -0.07782426914647306,
433
+ 0.14090289318728444,
434
+ -0.3634922073369279,
435
+ -0.08438139204305031,
436
+ ],
437
+ )
438
+ pI1, pI2 = (
439
+ [
440
+ -0.02274814414922734,
441
+ 0.048462952908062046,
442
+ -0.046965058282604985,
443
+ -0.19478935830847588,
444
+ ],
445
+ [
446
+ 0.025124060889537177,
447
+ -0.048672562735374666,
448
+ -1.199591061144479,
449
+ -0.1947893583084762,
450
+ ],
451
+ )
452
+
453
+ cat['Bmag'] = (
454
+ cat['gmag']
455
+ + np.polyval(pB1, cat['gmag'] - cat['rmag'])
456
+ + np.polyval(pB2, cat['rmag'] - cat['imag'])
457
+ )
458
+ cat['Vmag'] = (
459
+ cat['gmag']
460
+ + np.polyval(pV1, cat['gmag'] - cat['rmag'])
461
+ + np.polyval(pV2, cat['rmag'] - cat['imag'])
462
+ )
463
+ cat['Rmag'] = (
464
+ cat['rmag']
465
+ + np.polyval(pR1, cat['gmag'] - cat['rmag'])
466
+ + np.polyval(pR2, cat['rmag'] - cat['imag'])
467
+ )
468
+ cat['Imag'] = (
469
+ cat['imag']
470
+ + np.polyval(pI1, cat['gmag'] - cat['rmag'])
471
+ + np.polyval(pI2, cat['imag'] - cat['zmag'])
472
+ )
473
+
474
+ cat['e_Bmag'] = cat['e_gmag']
475
+ cat['e_Vmag'] = cat['e_gmag']
476
+ cat['e_Rmag'] = cat['e_rmag']
477
+ cat['e_Imag'] = cat['e_imag']
478
+
479
+ # Copies of columns for convenience
480
+ for _ in ['B', 'V', 'R', 'I']:
481
+ cat[_] = cat[_ + 'mag']
482
+
483
+ elif catalog == 'apass':
484
+ log("Augmenting the catalogue with Cousins R and I photometry")
485
+
486
+ # My own fit based on Landolt standards
487
+ cat['Rmag'] = (
488
+ cat['r_mag']
489
+ - 0.157
490
+ - 0.087 * (cat['g_mag'] - cat['r_mag'])
491
+ - 0.014 * (cat['g_mag'] - cat['r_mag']) ** 2
492
+ )
493
+ cat['e_Rmag'] = cat['e_r_mag']
494
+
495
+ cat['Imag'] = (
496
+ cat['i_mag']
497
+ - 0.354
498
+ - 0.118 * (cat['g_mag'] - cat['r_mag'])
499
+ - 0.004 * (cat['g_mag'] - cat['r_mag']) ** 2
500
+ )
501
+ cat['e_Imag'] = cat['e_i_mag']
502
+
503
+ # Copies of columns for convenience
504
+ for _ in ['B', 'V', 'R', 'I']:
505
+ cat[_] = cat[_ + 'mag']
506
+
507
+ elif catalog == 'gaiadr3syn':
508
+ # Compute magnitude errors from flux errors
509
+ for name in ['U', 'B', 'V', 'R', 'I', 'u', 'g', 'r', 'i', 'z', 'y']:
510
+ cat['e_' + name + 'mag'] = (
511
+ 2.5 / np.log(10) * cat['e_F' + name] / cat['F' + name]
512
+ )
513
+
514
+ log("Converting the catalogue Sloan magnitudes to Pan-STARRS ones")
515
+
516
+ # umag, gmag, rmag, imag and zmag are Sloan ugriz magnitudes! Let's get PS1 ones instead
517
+ # Fits are based on clean Landolt sample from https://arxiv.org/pdf/1203.0297.pdf
518
+ pg = [-0.030414391501015867, -0.09960002492299584, -0.002910024005294562]
519
+ pr = [-0.009566553708653305, 0.014924591443344211, -0.003928147919030857]
520
+ pi = [-0.010802807724098494, 0.01124900218746879, 0.01274293783734852]
521
+ pz = [-0.0031896767661109523, 0.06537983414287968, 0.007695587806229381]
522
+
523
+ for _ in ['u', 'g', 'r', 'i', 'z']:
524
+ cat[_ + '_SDSS'] = cat[_ + 'mag']
525
+
526
+ cat['gmag'] = cat['g_SDSS'] + np.polyval(pg, cat['g_SDSS'] - cat['r_SDSS'])
527
+ cat['rmag'] = cat['r_SDSS'] + np.polyval(pr, cat['g_SDSS'] - cat['r_SDSS'])
528
+ cat['imag'] = cat['i_SDSS'] + np.polyval(pi, cat['g_SDSS'] - cat['r_SDSS'])
529
+ cat['zmag'] = cat['z_SDSS'] + np.polyval(pz, cat['g_SDSS'] - cat['r_SDSS'])
530
+
531
+ return cat
532
+
533
+
534
+ def xmatch_objects(obj, catalog='ps1', sr=3 / 3600, col_ra='ra', col_dec='dec'):
535
+ """Cross-match object list with Vizier catalogue using CDS XMatch service.
536
+
537
+ Any Vizier catalogue may be used for cross-matching.
538
+
539
+ :param obj: astropy.table.Table with objects
540
+ :param catalog: Any Vizier catalogue identifier, or a catalogue short name
541
+ :param sr: Cross-matching radius in degrees
542
+ :param col_ra: Column name in `obj` table containing Right Ascension values
543
+ :param col_dec: Column name in `obj` table containing Declination values
544
+ :returns: The table of matched objects augmented with some fields from the Vizier catalogue.
545
+ """
546
+
547
+ if catalog in catalogs:
548
+ vizier_id = catalogs.get(catalog)['vizier']
549
+ else:
550
+ vizier_id = catalog
551
+
552
+ xcat = XMatch().query(
553
+ cat1=obj,
554
+ cat2='vizier:' + vizier_id,
555
+ max_distance=sr * u.deg,
556
+ colRA1=col_ra,
557
+ colDec1=col_dec,
558
+ )
559
+
560
+ return xcat
561
+
562
+
563
+ def xmatch_skybot(
564
+ obj, sr=10 / 3600, time=None, col_ra='ra', col_dec='dec', col_id='id'
565
+ ):
566
+ """Cross-match object list with positions of Solar System objects using SkyBoT service
567
+
568
+ The routine works by requesting the list of all solar system objects in a cone containing all
569
+ input objects, and then cross-matching them using the radius defined by `sr` parameter. Then it
570
+ returns the table of solar system objects plus a column of unique identifiers corresponding
571
+ to user objects.
572
+
573
+ :param obj: astropy.table.Table with objects
574
+ :param sr: Cross-matching radius in degrees
575
+ :param time: Time of the observation corresponding to the objects
576
+ :param col_ra: Column name in `obj` table containing Right Ascension values
577
+ :param col_dec: Column name in `obj` table containing Declination values
578
+ :param col_id: Column name in `obj` table containing some unique object identifier
579
+ :returns: The table of solar system objects augmented with `col_id` column of matched objects.
580
+ """
581
+
582
+ ra0, dec0, sr0 = astrometry.get_objects_center(obj)
583
+
584
+ try:
585
+ # Query SkyBot for (a bit larger than) our FOV at our time
586
+ xcat = Skybot.cone_search(
587
+ SkyCoord(ra0, dec0, unit='deg'), (sr0 + 2.0 * sr) * u.deg, Time(time)
588
+ )
589
+ except (RuntimeError, KeyError, ConnectionError, OSError):
590
+ # Nothing found in SkyBot
591
+ return None
592
+
593
+ # Cross-match objects
594
+ oidx, cidx, dist = astrometry.spherical_match(
595
+ obj[col_ra], obj[col_dec], xcat['RA'], xcat['DEC'], 10 / 3600
596
+ )
597
+
598
+ # Annotate the table with id from objects so that it is possible to identify the matches
599
+ xcat[col_id] = MaskedColumn(len(xcat), dtype=np.dtype(obj[col_id][0]))
600
+ xcat[col_id].mask = True
601
+ xcat[col_id][cidx] = obj[col_id][oidx]
602
+ xcat[col_id][cidx].mask = False
603
+
604
+ return xcat
605
+
606
+
607
+ def xmatch_ned(obj, sr=3 / 3600, col_ra='ra', col_dec='dec', col_id='id'):
608
+ """Cross-match object list with NED database entries
609
+
610
+ The routine is extremely inefficient as it has to query the objects one by one!
611
+
612
+ :param obj: astropy.table.Table with objects
613
+ :param sr: Cross-matching radius in degrees
614
+ :param col_ra: Column name in `obj` table containing Right Ascension values
615
+ :param col_dec: Column name in `obj` table containing Declination values
616
+ :param col_id: Column name in `obj` table containing some unique object identifier
617
+ :returns: The table of NED objects augmented with `id` column containing the identifiers from `col_id` columns of matched objects.
618
+
619
+ """
620
+
621
+ xcat = []
622
+
623
+ # FIXME: is there more optimal way to query NED for multiple sky positions?..
624
+ for row in obj:
625
+ res = Ned().query_region(
626
+ SkyCoord(row[col_ra], row[col_dec], unit='deg'), sr * u.deg
627
+ )
628
+
629
+ if res:
630
+ res['id'] = row[col_id]
631
+
632
+ xcat.append(res)
633
+
634
+ if xcat:
635
+ xcat = vstack(xcat, join_type='exact', metadata_conflicts='silent')
636
+
637
+ return xcat