astro-otter 0.6.0__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.
otter/util.py ADDED
@@ -0,0 +1,850 @@
1
+ """
2
+ Some constants, mappings, and functions to be used across the software
3
+ """
4
+
5
+ from __future__ import annotations
6
+ from itertools import chain
7
+ import os
8
+ from multiprocessing import Pool
9
+ import ads
10
+ from ads.exceptions import APIResponseError
11
+ import json
12
+ import astropy.units as u
13
+ import numpy as np
14
+
15
+ """
16
+ Helper functions first that just don't belong anywhere else
17
+ """
18
+
19
+
20
+ def filter_to_obstype(band_name):
21
+ """
22
+ Converts a band name to either 'radio', 'uvoir', 'xray'
23
+ """
24
+
25
+ try:
26
+ wave_eff = FILTER_MAP_WAVE[band_name] * u.nm
27
+ except KeyError as exc:
28
+ raise Exception(
29
+ f"No Effective Wavelength Known for {band_name}, please add it to constants"
30
+ ) from exc
31
+
32
+ return wave_to_obstype(wave_eff)
33
+
34
+
35
+ def freq_to_obstype(freq_eff):
36
+ """
37
+ Converts a frequency value to either 'radio', 'uvoir', 'xray'
38
+
39
+ Args:
40
+ freq_eff (u.Quantity) : An astropy quantity in frequency space
41
+ """
42
+
43
+ wave_eff = freq_eff.to(u.nm, equivalencies=u.spectral())
44
+ return wave_to_obstype(wave_eff)
45
+
46
+
47
+ def wave_to_obstype(wave_eff):
48
+ """
49
+ Converts a wavelength value to either 'radio', 'uvoir', 'xray'
50
+
51
+ Args:
52
+ wave_eff (u.Quantity) : An astropy quantity in wavelength space
53
+ """
54
+ if wave_eff > 0.1 * u.mm:
55
+ return "radio"
56
+ elif wave_eff <= 0.1 * u.mm and wave_eff >= 10 * u.nm:
57
+ return "uvoir"
58
+ else:
59
+ return "xray"
60
+
61
+
62
+ def clean_schema(schema):
63
+ """
64
+ Clean out Nones and empty lists from the given subschema
65
+ """
66
+ for key, val in list(schema.items()):
67
+ if val is None or (isinstance(val, (list, dict)) and len(val) == 0):
68
+ del schema[key]
69
+ return schema
70
+
71
+
72
+ def bibcode_to_hrn(bibcode, local_reference_map="reference_map_local.json"):
73
+ if isinstance(bibcode, str):
74
+ bibcode = [bibcode]
75
+
76
+ if os.path.exists(local_reference_map):
77
+ with open(local_reference_map, "r") as j:
78
+ local_map = json.load(j)
79
+ else:
80
+ local_map = {}
81
+
82
+ hrns = []
83
+ bibcodes = []
84
+ bibcodes_to_query = []
85
+ for b in bibcode:
86
+ if b in local_map:
87
+ hrns.append(local_map[b])
88
+ bibcodes.append(b)
89
+ else:
90
+ bibcodes_to_query.append(b)
91
+
92
+ if len(bibcodes_to_query) > 0:
93
+ queried_bibs, queried_hrns = _bibcode_to_hrn_with_query(bibcodes_to_query)
94
+ hrns += queried_hrns
95
+ bibcodes += queried_bibs
96
+
97
+ return bibcodes, hrns
98
+
99
+
100
+ def _bibcode_to_hrn_with_query(bibcode):
101
+ """
102
+ Converts a bibcode to a human_readable_name (hrn) using ADSQuery
103
+ """
104
+ if isinstance(bibcode, str):
105
+ bibcode = bibcode.strip()
106
+ query = f"bibcode:{bibcode}"
107
+ bibcodes = [bibcode]
108
+ else:
109
+ # make sure the bibcodes are lists instead of strings of lists
110
+ bibcode = [b.strip("[]").replace("'", "").split(", ") for b in bibcode]
111
+
112
+ bibcode = list(chain(*[v if isinstance(v, list) else [v] for v in bibcode]))
113
+
114
+ bibcodes_flat = np.array(bibcode).flatten()
115
+ bibcodes_cleaned = np.array([b.strip() for b in bibcodes_flat])
116
+
117
+ bibcodes = list(np.unique(bibcodes_cleaned))
118
+
119
+ protected_vals = ["private", "new", "current work"]
120
+ for val in protected_vals:
121
+ if val in bibcodes:
122
+ bibcodes.pop(bibcodes.index(val))
123
+
124
+ if len(bibcodes) == 0:
125
+ # then all of the bibcodes were "private"
126
+ return bibcodes, bibcodes
127
+
128
+ query = f"bibcode:{bibcodes[0]}"
129
+ if len(bibcodes) > 1:
130
+ for b in bibcodes[1:]:
131
+ query += f" OR {b}"
132
+
133
+ try:
134
+ qobj = ads.SearchQuery(q=query)
135
+ qobj.execute() # do the query
136
+ adsquery = list(qobj)
137
+ except IndexError:
138
+ raise ValueError(f"Could not find {bibcode} on ADS!")
139
+ except APIResponseError as exc:
140
+ raise ValueError(
141
+ f"""Failed on query {query}! \n Potentially out of ADS
142
+ queries! Run curl command to check like\n
143
+ https://github.com/adsabs/adsabs-dev-api/blob/master/README.md"""
144
+ ) from exc
145
+
146
+ hrns = []
147
+ bibcodes_to_return = []
148
+ for res in adsquery:
149
+ authors = res.author
150
+ year = res.year
151
+
152
+ if len(authors) == 0:
153
+ raise ValueError("This ADS bibcode does not exist!")
154
+ elif len(authors) == 1:
155
+ author = authors[0]
156
+ elif len(authors) == 2:
157
+ author = authors[0] + " & " + authors[1]
158
+ else: # longer than 2
159
+ author = authors[0] + " et al."
160
+
161
+ # generate the human readable name
162
+ hrn = author + " (" + year + ")"
163
+ hrns.append(hrn)
164
+ bibcodes_to_return.append(res.bibcode)
165
+
166
+ if len(hrns) == 0 and len(bibcodes) != 0:
167
+ raise ValueError(f"Could not find any bibcodes associated with {bibcodes}!")
168
+
169
+ if isinstance(bibcode, str):
170
+ return hrns[0]
171
+
172
+ if len(hrns) != len(bibcodes):
173
+ raise ValueError(
174
+ f"ADS has multiple sources associated with one of these bibcodes! \
175
+ {bibcode}"
176
+ )
177
+
178
+ return bibcodes_to_return, hrns
179
+
180
+
181
+ def freq_to_band(freq: u.Quantity) -> str:
182
+ """
183
+ Converts an effective frequency to the corresponding band name based on the
184
+ standards listed in RADIO_BAND_MAPPING
185
+
186
+ Args:
187
+ freq (astropy Quantity) : Astropy Quantity of the frequency to get the band name
188
+ Returns:
189
+ string with the commonly used band name
190
+ """
191
+
192
+ for key, freq_range in RADIO_BAND_MAPPING.items():
193
+ if freq_range[0] * u.GHz < freq <= freq_range[1] * u.GHz:
194
+ return key
195
+
196
+ raise ValueError(f"No band name found for the frequency {freq}. Please verify that \
197
+ it is in a range in RADIO_BAND_MAPPING and, if not, add it!")
198
+
199
+
200
+ def _wrap_single_conversion(t):
201
+ """
202
+ t is a tuple of (frequency, unit)
203
+ """
204
+ return freq_to_band(t[0] * u.Unit(t[1]))
205
+
206
+
207
+ def freqlist_to_band(
208
+ freq_list: list[float], freq_unit_list: list[str], ncores=1
209
+ ) -> list[str]:
210
+ """
211
+ Converts a list of effective frequencies to the corresponding band names based on
212
+ the standards listed in RADIO_BAND_MAPPING
213
+
214
+ Args:
215
+ freq_list (list[float]): floats for the frequencies
216
+ freq_unit_list (list[str]): List of astropy unit strings to apply to freq_list
217
+ ncores (int): The number of cores to multiprocess with
218
+
219
+ Returns:
220
+ list of strings with the band names
221
+ """
222
+ with Pool(ncores) as p:
223
+ return p.map(_wrap_single_conversion, zip(freq_list, freq_unit_list))
224
+
225
+
226
+ """
227
+ Then the constants and dictionary mappings used throughout
228
+ """
229
+
230
+ # gives the effective wavelength for each filter given
231
+ # these are all in nanometers!
232
+ FILTER_MAP_WAVE = {
233
+ "FUV": 153.8620701901866,
234
+ "NUV": 231.56631043707714,
235
+ "UVW2": 207.98754332676123,
236
+ "uvw2": 207.98754332676123,
237
+ "W2": 207.98754332676123,
238
+ "2": 207.98754332676123,
239
+ "uw2": 207.98754332676123,
240
+ "UVM2": 225.47802478793594,
241
+ "uvm2": 225.47802478793594,
242
+ "M2": 225.47802478793594,
243
+ "M": 225.47802478793594,
244
+ "um2": 225.47802478793594,
245
+ "UVW1": 261.3713060531025,
246
+ "uvw1": 261.3713060531025,
247
+ "W1": 261.3713060531025,
248
+ "1": 261.3713060531025,
249
+ "uw1": 261.3713060531025,
250
+ "u": 356.17887353001856,
251
+ "u'": 356.17887353001856,
252
+ "up": 356.17887353001856,
253
+ "uprime": 356.17887353001856,
254
+ "U_S": 347.06360491031495,
255
+ "s": 347.06360491031495,
256
+ "us": 347.06360491031495,
257
+ "U": 353.10502283105023,
258
+ "B": 443.0539845758355,
259
+ "B_S": 435.912081730874,
260
+ "b": 435.912081730874,
261
+ "bs": 435.912081730874,
262
+ "g": 471.8872246248687,
263
+ "g'": 471.8872246248687,
264
+ "gp": 471.8872246248687,
265
+ "gprime": 471.8872246248687,
266
+ "F475W": 471.8872246248687,
267
+ "g-DECam": 482.6787274749997,
268
+ "c": 540.8724658332794,
269
+ "cyan": 540.8724658332794,
270
+ "V": 553.7155963302753,
271
+ "V_S": 543.0131091205997,
272
+ "v": 543.0131091205997,
273
+ "vs": 543.0131091205997,
274
+ "Itagaki": 651.0535687558726,
275
+ "white": 752.0,
276
+ "unfilt.": 616.690135,
277
+ "0": 616.690135,
278
+ "C": 616.690135,
279
+ "clear": 616.690135,
280
+ "pseudobolometric": 616.690135,
281
+ "griz": 616.690135,
282
+ "RGB": 616.690135,
283
+ "LGRB": 616.690135,
284
+ "G": 673.5412573108297,
285
+ "Kepler": 641.6835660569259,
286
+ "TESS": 797.2360657697333,
287
+ "DLT40": 615.8130149792426,
288
+ "Open": 615.8130149792426,
289
+ "Clear": 615.8130149792426,
290
+ "w": 638.9300625093831,
291
+ "o": 686.6260690394873,
292
+ "orange": 686.6260690394873,
293
+ "r": 618.5194476741524,
294
+ "r'": 618.5194476741524,
295
+ "rp": 618.5194476741524,
296
+ "rprime": 618.5194476741524,
297
+ "F625W": 618.5194476741524,
298
+ "r-DECam": 643.2062638192127,
299
+ "R": 646.9439215118385,
300
+ "Rc": 646.9439215118385,
301
+ "R_s": 646.9439215118385,
302
+ "i": 749.9704174464691,
303
+ "i'": 749.9704174464691,
304
+ "ip": 749.9704174464691,
305
+ "iprime": 749.9704174464691,
306
+ "F775W": 749.9704174464691,
307
+ "i-DECam": 782.6680306208917,
308
+ "I": 788.558706467662,
309
+ "Ic": 788.558706467662,
310
+ "z_s": 867.9495480864285,
311
+ "zs": 867.9495480864285,
312
+ "z": 896.1488333992431,
313
+ "z'": 896.1488333992431,
314
+ "zp": 896.1488333992431,
315
+ "zprime": 896.1488333992431,
316
+ "z-DECam": 917.8949537472383,
317
+ "y": 963.3308299506817,
318
+ "y-DECam": 989.965087304703,
319
+ "J": 1255.0918319447906,
320
+ "H": 1630.5155019191195,
321
+ "K": 2157.3600605745955,
322
+ "Ks": 2157.3600605745955,
323
+ "F070W": 705.5727879998312,
324
+ "F090W": 904.2281265089156,
325
+ "F115W": 1157.001589027877,
326
+ "F150W": 1503.9880463410511,
327
+ "F200W": 1993.3922957570885,
328
+ "F225W": 2372.81,
329
+ "F277W": 2769.332372846113,
330
+ "F300M": 2990.7606605760484,
331
+ "F335M": 3363.887076210947,
332
+ "F356W": 3576.787256375927,
333
+ "F360M": 3626.0578695682693,
334
+ "F444W": 4415.974447587756,
335
+ "F560W": 5645.279496731566,
336
+ "F770W": 7663.455798629626,
337
+ "F1000W": 9968.161727011531,
338
+ "F1130W": 11310.984595876938,
339
+ "F1280W": 12831.396996921212,
340
+ "F1500W": 15091.367399905488,
341
+ "F1800W": 18006.083119653664,
342
+ "F2100W": 20842.526633138932,
343
+ "F2550W": 25408.228367890282,
344
+ }
345
+ """
346
+ Mapping for the effective wavelength in nanometers for all filters used in the dataset.
347
+ """
348
+
349
+
350
+ # gives the effective frequency for all filters
351
+ # These are all in THz
352
+ FILTER_MAP_FREQ = {
353
+ "FUV": 1975.086895569116,
354
+ "NUV": 1346.3548820463916,
355
+ "UVW2": 1531.4976984760474,
356
+ "uvw2": 1531.4976984760474,
357
+ "W2": 1531.4976984760474,
358
+ "2": 1531.4976984760474,
359
+ "uw2": 1531.4976984760474,
360
+ "UVM2": 1360.083095675749,
361
+ "uvm2": 1360.083095675749,
362
+ "M2": 1360.083095675749,
363
+ "M": 1360.083095675749,
364
+ "um2": 1360.083095675749,
365
+ "UVW1": 1236.8527545450256,
366
+ "uvw1": 1236.8527545450256,
367
+ "W1": 1236.8527545450256,
368
+ "1": 1236.8527545450256,
369
+ "uw1": 1236.8527545450256,
370
+ "u": 849.2871562331687,
371
+ "u'": 849.2871562331687,
372
+ "up": 849.2871562331687,
373
+ "uprime": 849.2871562331687,
374
+ "U_S": 875.611103788721,
375
+ "s": 875.611103788721,
376
+ "us": 875.611103788721,
377
+ "U": 858.321721875779,
378
+ "B": 688.8500955332158,
379
+ "B_S": 696.7876979144597,
380
+ "b": 696.7876979144597,
381
+ "bs": 696.7876979144597,
382
+ "g": 648.9823425403824,
383
+ "g'": 648.9823425403824,
384
+ "gp": 648.9823425403824,
385
+ "gprime": 648.9823425403824,
386
+ "F475W": 648.9823425403824,
387
+ "g-DECam": 635.8015668464043,
388
+ "c": 580.1132515050684,
389
+ "cyan": 580.1132515050684,
390
+ "V": 548.3068934496129,
391
+ "V_S": 554.9815375506427,
392
+ "v": 554.9815375506427,
393
+ "vs": 554.9815375506427,
394
+ "Itagaki": 577.0861573682259,
395
+ "white": 30079.243284322874,
396
+ "unfilt.": 601.5655810567023,
397
+ "0": 601.5655810567023,
398
+ "C": 601.5655810567023,
399
+ "clear": 601.5655810567023,
400
+ "pseudobolometric": 601.5655810567023,
401
+ "griz": 601.5655810567023,
402
+ "RGB": 601.5655810567023,
403
+ "LGRB": 601.5655810567023,
404
+ "G": 518.6766845466752,
405
+ "Kepler": 519.5058608954615,
406
+ "TESS": 403.1881955125893,
407
+ "DLT40": 629.637672549936,
408
+ "Open": 629.637672549936,
409
+ "Clear": 629.637672549936,
410
+ "w": 520.8387777057242,
411
+ "o": 451.71177203298663,
412
+ "orange": 451.71177203298663,
413
+ "r": 489.2629992899134,
414
+ "r'": 489.2629992899134,
415
+ "rp": 489.2629992899134,
416
+ "rprime": 489.2629992899134,
417
+ "F625W": 489.2629992899134,
418
+ "r-DECam": 472.4459671948087,
419
+ "R": 471.26223689126897,
420
+ "Rc": 471.26223689126897,
421
+ "R_s": 471.26223689126897,
422
+ "i": 402.8409598867557,
423
+ "i'": 402.8409598867557,
424
+ "ip": 402.8409598867557,
425
+ "iprime": 402.8409598867557,
426
+ "F775W": 402.8409598867557,
427
+ "i-DECam": 386.62233825433924,
428
+ "I": 382.7915178046724,
429
+ "Ic": 382.7915178046724,
430
+ "z_s": 346.66628641927826,
431
+ "zs": 346.66628641927826,
432
+ "z": 337.7343708777923,
433
+ "z'": 337.7343708777923,
434
+ "zp": 337.7343708777923,
435
+ "zprime": 337.7343708777923,
436
+ "z-DECam": 328.753462451287,
437
+ "y": 312.24818210606065,
438
+ "y-DECam": 303.4727730182509,
439
+ "J": 239.862442505934,
440
+ "H": 185.33613196897403,
441
+ "K": 139.79431978859097,
442
+ "Ks": 139.79431978859097,
443
+ "F070W": 431.04176743403116,
444
+ "F090W": 336.17431986268366,
445
+ "F115W": 262.87628654288676,
446
+ "F150W": 201.94374815011136,
447
+ "F200W": 152.56522352568953,
448
+ "F277W": 110.05136786468209,
449
+ "F300M": 100.56915203596012,
450
+ "F335M": 89.41072625742719,
451
+ "F356W": 85.01984846997881,
452
+ "F360M": 82.9357933095218,
453
+ "F444W": 68.96667222373961,
454
+ "F560W": 53.67852315133938,
455
+ "F770W": 39.87175477126777,
456
+ "F1000W": 30.349460503852665,
457
+ "F1130W": 26.53952983680919,
458
+ "F1280W": 23.59741975845449,
459
+ "F1500W": 20.08679352819493,
460
+ "F1800W": 16.773842151606242,
461
+ "F2100W": 14.581938602646188,
462
+ "F2550W": 11.919267708332558,
463
+ }
464
+ """
465
+ Mapping for the effective frequencies in THz for all the filters used in OTTER
466
+ """
467
+
468
+
469
+ # x-ray telescope areas for converting
470
+ # NOTE: these are estimates from the links provided
471
+ # Since this is inherently instrument dependent they are not entirely reliable
472
+ # All are for 1-2 keV
473
+ XRAY_AREAS = {
474
+ # https://swift.gsfc.nasa.gov/about_swift/Sci_Fact_Sheet.pdf
475
+ "swift": 135 * u.cm**2,
476
+ "swift-xrt": 135 * u.cm**2,
477
+ # https://heasarc.gsfc.nasa.gov/docs/rosat/ruh/handbook/node39.html#SECTION00634000000000000000
478
+ "rosat": 400 * u.cm**2,
479
+ # https://www.cosmos.esa.int/web/xmm-newton/technical-details-mirrors
480
+ "xmm": 1500 * u.cm**2,
481
+ "xmm slew": 1500 * u.cm**2,
482
+ "xmm-slew": 1500 * u.cm**2,
483
+ "xmm pointed": 1500 * u.cm**2,
484
+ "xmm-newton": 1500 * u.cm**2,
485
+ # https://cxc.harvard.edu/cdo/about_chandra
486
+ "chandra": 600 * u.cm**2,
487
+ # https://www.cosmos.esa.int/documents/332006/954765/Brunner_TopicK.pdf
488
+ "erosita": 1500 * u.cm**2,
489
+ # https://en.wikipedia.org/wiki/NuSTAR
490
+ "nustar": 847 * u.cm**2,
491
+ # https://iss.jaxa.jp/en/kiboexp/ef/maxi/
492
+ "maxi": 200 * u.cm**2,
493
+ # https://iopscience.iop.org/article/10.3847/1538-4357/abd569
494
+ "konus-wind": 120 * u.cm**2,
495
+ # https://www.cosmos.esa.int/web/einstein-probe/mission
496
+ "ep": 600 * u.cm**2,
497
+ "ep-fxt": 600 * u.cm**2,
498
+ }
499
+ """
500
+ X-Ray telescope areas that are used for converting from counts to other units.
501
+
502
+ NOTE: These are estimates from the following links
503
+ * https://swift.gsfc.nasa.gov/about_swift/Sci_Fact_Sheet.pdf
504
+ * https://heasarc.gsfc.nasa.gov/docs/rosat/ruh/handbook/node39.html#SECTION00634000000000000000
505
+ * https://www.cosmos.esa.int/web/xmm-newton/technical-details-mirrors
506
+ * https://cxc.harvard.edu/cdo/about_chandra
507
+ """
508
+
509
+ # Radio Band names with frequency ranges in GHz
510
+ RADIO_BAND_MAPPING = {
511
+ # use some lower bands from IEEE standards up to GMRT standards
512
+ # These are mostly here just as catchalls for low frequency radio telescopes
513
+ # like GEETEE or LOFAR
514
+ "HF": (0.003, 0.03),
515
+ "VHF": (0.03, 0.125), # Official: (0.03, 0.3) but adjust to work with GMRT standard
516
+ # use GMRT standards for low frequencies
517
+ "gmrt.2": (0.125, 0.250),
518
+ "gmrt.3": (0.25, 0.55), # Official: (0.25, 0.5)
519
+ # IEEE Standard Naming
520
+ # https://terasense.com/terahertz-technology/radio-frequency-bands/
521
+ "UHF": (0.55, 1),
522
+ "L": (1, 2),
523
+ "S": (2, 4),
524
+ "C": (4, 8),
525
+ "X": (8, 12),
526
+ "Ku": (12, 18),
527
+ "K": (18, 27),
528
+ "Ka": (27, 40),
529
+ # then switch to ALMA standard for mm
530
+ # https://www.eso.org/public/teles-instr/alma/receiver-bands/
531
+ # I widened some of the official ranges slightly so we don't leave gaps
532
+ # although these gaps probably wouldn't have mattered because **atmosphere**
533
+ "alma.1": (40, 50), # Official: (35, 50)
534
+ "alma.3": (84, 116), # skip alma.2 cause it's just a smaller range of alma.3
535
+ "alma.4": (116, 163), # official: (125, 163)
536
+ "alma.5": (163, 211),
537
+ "alma.6": (211, 275),
538
+ "alma.7": (275, 373),
539
+ "alma.8": (373, 500), # official: (385,500)
540
+ "alma.9": (500, 720), # official: (602, 720)
541
+ "alma.10": (787, 950),
542
+ }
543
+ """
544
+ Mapping of common radio/mm band names to their corresponding frequency ranges.
545
+ All frequencies are in GHz. The upperlimit is inclusive, that is these ranges
546
+ are really (xx, yy].
547
+ """
548
+
549
+ # define a working base directory constant
550
+ BASEDIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
551
+ """
552
+ Base directory for the OTTER API software package
553
+ """
554
+
555
+ VIZIER_LARGE_CATALOGS = [
556
+ "2MASS-PSC",
557
+ "2MASX",
558
+ "AC2000.2",
559
+ "AKARI",
560
+ "ALLWISE",
561
+ "ASCC-2.5",
562
+ "B/DENIS",
563
+ "CMC14",
564
+ "Gaia-DR1",
565
+ "GALEX",
566
+ "GLIMPSE",
567
+ "GSC-ACT",
568
+ "GSC1.2",
569
+ "GSC2.2",
570
+ "GSC2.3",
571
+ "HIP",
572
+ "HIP2",
573
+ "IRAS",
574
+ "NOMAD1",
575
+ "NVSS",
576
+ "PanSTARRS-DR1",
577
+ "PGC",
578
+ "Planck-DR1",
579
+ "PPMX",
580
+ "PPMXL",
581
+ "SDSS-DR12",
582
+ "SDSS-DR7",
583
+ "SDSS-DR9",
584
+ "Tycho-2",
585
+ "UCAC2",
586
+ "UCAC3",
587
+ "UCAC4",
588
+ "UKIDSS",
589
+ "USNO-A2",
590
+ "USNO-B1",
591
+ "WISE",
592
+ ]
593
+ """
594
+ ViZier catalog names that we query for host information in the Host class
595
+ """
596
+
597
+ _KNOWN_CLASS_ROOTS = [
598
+ "SN",
599
+ "SLSN",
600
+ "TDE",
601
+ "GRB",
602
+ "LGRB",
603
+ "SGRB",
604
+ "AGN",
605
+ "FRB",
606
+ "QSO",
607
+ "ANT",
608
+ ]
609
+ """
610
+ Classification root names
611
+ """
612
+
613
+ DATADIR = os.path.join(BASEDIR, "data", "base")
614
+ """
615
+ Deprecated database directory that IS NOT always constant anymore
616
+ """
617
+
618
+ ####################################################################
619
+ # The following schema dictionaries are here so the code remains
620
+ # backwards compatible. BUT! All future code should use the pydantic
621
+ # schema defined in schema.py
622
+ ####################################################################
623
+
624
+ # Overarching schema that stops once we get down to a string or list
625
+ schema = {
626
+ "schema_version": {"value": "0", "comment": "Copied from tde.space"},
627
+ "name": {"default_name": None, "alias": []},
628
+ "coordinate": [],
629
+ "distance": [],
630
+ "classification": {"value": []},
631
+ "reference_alias": [],
632
+ "date_reference": [],
633
+ "photometry": [],
634
+ "spectra": [],
635
+ "filter_alias": [],
636
+ }
637
+ """
638
+ Schema dictionary to be filled with values from the subschemas
639
+ """
640
+
641
+ # sub schemas that get filled into lists
642
+ name_alias_schema = {"value": None, "reference": None}
643
+ """
644
+ Subschema for the name and alias dictionary
645
+ """
646
+
647
+ coordinate_schema = {
648
+ "ra": None,
649
+ "dec": None,
650
+ "l": None,
651
+ "b": None,
652
+ "lon": None,
653
+ "lat": None,
654
+ "ra_units": None,
655
+ "dec_units": None,
656
+ "l_units": None,
657
+ "b_units": None,
658
+ "lon_units": None,
659
+ "lat_units": None,
660
+ "ra_error": None,
661
+ "dec_error": None,
662
+ "l_error": None,
663
+ "b_error": None,
664
+ "lon_error": None,
665
+ "lat_error": None,
666
+ "epoch": None,
667
+ "frame": None,
668
+ "coord_type": None,
669
+ "computed": None,
670
+ "reference": None,
671
+ "default": None,
672
+ }
673
+ """
674
+ Subschema to describe the possible attributes for the coordinate dictionary
675
+ """
676
+
677
+
678
+ distance_schema = {
679
+ "value": None,
680
+ "unit": None,
681
+ "error": None,
682
+ "cosmology": None,
683
+ "reference": None,
684
+ "computed": None,
685
+ "uuid": None,
686
+ "default": None,
687
+ "distance_type": None,
688
+ }
689
+ """
690
+ Subschema to describe the possible attributes for the distance dictionary
691
+ """
692
+
693
+
694
+ classification_schema = {
695
+ "object_class": None,
696
+ "confidence": None,
697
+ "class_type": None,
698
+ "reference": None,
699
+ "default": None,
700
+ }
701
+ """
702
+ Subschema to describe the attributes for the classification dictionary
703
+ """
704
+
705
+ reference_alias_schema = {"name": None, "human_readable_name": None}
706
+ """
707
+ Subschema to describe the attributes for the reference alias dictionary
708
+ """
709
+
710
+ date_reference_schema = {
711
+ "value": None,
712
+ "date_format": None,
713
+ "date_type": None,
714
+ "reference": None,
715
+ "computed": None,
716
+ }
717
+ """
718
+ Subschema to describe the date_reference dictionary attributes
719
+ """
720
+
721
+ photometry_schema = {
722
+ "raw": None,
723
+ "raw_err": None,
724
+ "raw_units": None,
725
+ "value": None,
726
+ "value_err": None,
727
+ "value_units": None,
728
+ "epoch_zeropoint": None,
729
+ "epoch_redshift": None,
730
+ "filter": None,
731
+ "filter_key": None,
732
+ "obs_type": None,
733
+ "telescope_area": None,
734
+ "date": None,
735
+ "date_format": None,
736
+ "date_err": None,
737
+ "ignore": None,
738
+ "upperlimit": None,
739
+ "sigma": None,
740
+ "sky": None,
741
+ "telescope": None,
742
+ "instrument": None,
743
+ "phot_type": None,
744
+ "exptime": None,
745
+ "aperature": None,
746
+ "observer": None,
747
+ "reducer": None,
748
+ "pipeline": None,
749
+ "corr_k": None,
750
+ "corr_av": None,
751
+ "corr_host": None,
752
+ "corr_hostav": None,
753
+ "val_k": None,
754
+ "val_s": None,
755
+ "val_av": None,
756
+ "val_host": None,
757
+ "val_hostav": None,
758
+ }
759
+ """
760
+ Subschema to describe all of the possible attributes that can be used in the photometry
761
+ dictionary
762
+ """
763
+
764
+ spectra_schema = {
765
+ "wavelength": None,
766
+ "wavelength_units": None,
767
+ "flux": None,
768
+ "fluxerr": None,
769
+ "raw": None,
770
+ "raw_err": None,
771
+ "sky": None,
772
+ "lamp": None,
773
+ "flux_units": None,
774
+ "telescope": None,
775
+ "instrument": None,
776
+ "date": None,
777
+ "date_format": None,
778
+ "date_err": None,
779
+ "exptime": None,
780
+ "slit": None,
781
+ "airmass": None,
782
+ "disperser": None,
783
+ "resolution": None,
784
+ "resolution_units": None,
785
+ "min_wave": None,
786
+ "max_wave": None,
787
+ "filter": None,
788
+ "filter_key": None,
789
+ "standard_name": None,
790
+ "ignore": None,
791
+ "spec_type": None,
792
+ "aperture": None,
793
+ "observer": None,
794
+ "reducer": None,
795
+ "pipeline": None,
796
+ "corr_k": None,
797
+ "corr_av": None,
798
+ "corr_host": None,
799
+ "corr_hostav": None,
800
+ "corr_flux": None,
801
+ "corr_phot": None,
802
+ "val_k": None,
803
+ "val_av": None,
804
+ "val_host": None,
805
+ "val_hostav": None,
806
+ }
807
+
808
+ filter_alias_schema = {
809
+ "filter_key": None,
810
+ "wave_eff": None,
811
+ "wave_min": None,
812
+ "wave_max": None,
813
+ "freq_eff": None,
814
+ "freq_min": None,
815
+ "freq_max": None,
816
+ "zp": None,
817
+ "wave_units": None,
818
+ "freq_units": None,
819
+ "zp_units": None,
820
+ "zp_system": None,
821
+ }
822
+ """
823
+ Subschema to describe the attributes in the filter_alias dictionary
824
+ """
825
+
826
+ # package the subschemas by the key used for that location in the Transient object
827
+ subschema = {
828
+ "name/alias": name_alias_schema,
829
+ "coordinate": coordinate_schema,
830
+ "distance": distance_schema,
831
+ "classification": classification_schema,
832
+ "reference_alias": reference_alias_schema,
833
+ "date_reference": date_reference_schema,
834
+ "photometry": photometry_schema,
835
+ "spectra": spectra_schema,
836
+ "filter_alias": filter_alias_schema,
837
+ }
838
+ """
839
+ A useful variable to describe all of the subschemas that are available and can be used
840
+ """
841
+
842
+
843
+ class _DuplicateFilter(object):
844
+ def __init__(self):
845
+ self.msgs = set()
846
+
847
+ def filter(self, record):
848
+ rv = record.msg not in self.msgs
849
+ self.msgs.add(record.msg)
850
+ return rv