astro-otter 0.1.0__py3-none-any.whl → 0.2.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.

Potentially problematic release.


This version of astro-otter might be problematic. Click here for more details.

otter/util.py CHANGED
@@ -3,9 +3,14 @@ Some constants, mappings, and functions to be used across the software
3
3
  """
4
4
 
5
5
  from __future__ import annotations
6
+ from itertools import chain
6
7
  import os
8
+ from multiprocessing import Pool
7
9
  import ads
10
+ from ads.exceptions import APIResponseError
11
+ import json
8
12
  import astropy.units as u
13
+ import numpy as np
9
14
 
10
15
  """
11
16
  Helper functions first that just don't belong anywhere else
@@ -24,9 +29,31 @@ def filter_to_obstype(band_name):
24
29
  f"No Effective Wavelength Known for {band_name}, please add it to constants"
25
30
  ) from exc
26
31
 
27
- if wave_eff > 1 * u.mm:
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:
28
55
  return "radio"
29
- elif wave_eff <= 1 * u.mm and wave_eff >= 10 * u.nm:
56
+ elif wave_eff <= 0.1 * u.mm and wave_eff >= 10 * u.nm:
30
57
  return "uvoir"
31
58
  else:
32
59
  return "xray"
@@ -42,31 +69,154 @@ def clean_schema(schema):
42
69
  return schema
43
70
 
44
71
 
45
- def bibcode_to_hrn(bibcode):
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):
46
101
  """
47
102
  Converts a bibcode to a human_readable_name (hrn) using ADSQuery
48
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
+ query = f"bibcode:{bibcodes[0]}"
125
+ if len(bibcodes) > 1:
126
+ for b in bibcodes[1:]:
127
+ query += f" OR {b}"
49
128
 
50
129
  try:
51
- adsquery = list(ads.SearchQuery(bibcode=bibcode))[0]
130
+ qobj = ads.SearchQuery(q=query)
131
+ qobj.execute() # do the query
132
+ adsquery = list(qobj)
52
133
  except IndexError:
53
134
  raise ValueError(f"Could not find {bibcode} on ADS!")
135
+ except APIResponseError as exc:
136
+ raise ValueError(
137
+ f"""Failed on query {query}! \n Potentially out of ADS
138
+ queries! Run curl command to check like\n
139
+ https://github.com/adsabs/adsabs-dev-api/blob/master/README.md"""
140
+ ) from exc
141
+
142
+ hrns = []
143
+ bibcodes_to_return = []
144
+ for res in adsquery:
145
+ authors = res.author
146
+ year = res.year
54
147
 
55
- authors = adsquery.author
56
- year = adsquery.year
148
+ if len(authors) == 0:
149
+ raise ValueError("This ADS bibcode does not exist!")
150
+ elif len(authors) == 1:
151
+ author = authors[0]
152
+ elif len(authors) == 2:
153
+ author = authors[0] + " & " + authors[1]
154
+ else: # longer than 2
155
+ author = authors[0] + " et al."
57
156
 
58
- if len(authors) == 0:
59
- raise ValueError("This ADS bibcode does not exist!")
60
- elif len(authors) == 1:
61
- author = authors[0]
62
- elif len(authors) == 2:
63
- author = authors[0] + " & " + authors[1]
64
- else: # longer than 2
65
- author = authors[0] + " et al."
157
+ # generate the human readable name
158
+ hrn = author + " (" + year + ")"
159
+ hrns.append(hrn)
160
+ bibcodes_to_return.append(res.bibcode)
66
161
 
67
- # generate the human readable name
68
- hrn = author + " (" + year + ")"
69
- return hrn
162
+ if len(hrns) == 0 and len(bibcodes) != 0:
163
+ raise ValueError(f"Could not find any bibcodes associated with {bibcodes}!")
164
+
165
+ if isinstance(bibcode, str):
166
+ return hrns[0]
167
+
168
+ if len(hrns) != len(bibcodes):
169
+ raise ValueError(
170
+ f"ADS has multiple sources associated with one of these bibcodes! \
171
+ {bibcode}"
172
+ )
173
+
174
+ return bibcodes_to_return, hrns
175
+
176
+
177
+ def freq_to_band(freq: u.Quantity) -> str:
178
+ """
179
+ Converts an effective frequency to the corresponding band name based on the
180
+ standards listed in RADIO_BAND_MAPPING
181
+
182
+ Args:
183
+ freq (astropy Quantity) : Astropy Quantity of the frequency to get the band name
184
+ Returns:
185
+ string with the commonly used band name
186
+ """
187
+
188
+ for key, freq_range in RADIO_BAND_MAPPING.items():
189
+ if freq_range[0] * u.GHz < freq <= freq_range[1] * u.GHz:
190
+ return key
191
+
192
+ raise ValueError(f"No band name found for the frequency {freq}. Please verify that \
193
+ it is in a range in RADIO_BAND_MAPPING and, if not, add it!")
194
+
195
+
196
+ def _wrap_single_conversion(t):
197
+ """
198
+ t is a tuple of (frequency, unit)
199
+ """
200
+ return freq_to_band(t[0] * u.Unit(t[1]))
201
+
202
+
203
+ def freqlist_to_band(
204
+ freq_list: list[float], freq_unit_list: list[str], ncores=1
205
+ ) -> list[str]:
206
+ """
207
+ Converts a list of effective frequencies to the corresponding band names based on
208
+ the standards listed in RADIO_BAND_MAPPING
209
+
210
+ Args:
211
+ freq_list (list[float]): floats for the frequencies
212
+ freq_unit_list (list[str]): List of astropy unit strings to apply to freq_list
213
+ ncores (int): The number of cores to multiprocess with
214
+
215
+ Returns:
216
+ list of strings with the band names
217
+ """
218
+ with Pool(ncores) as p:
219
+ return p.map(_wrap_single_conversion, zip(freq_list, freq_unit_list))
70
220
 
71
221
 
72
222
  """
@@ -324,9 +474,13 @@ XRAY_AREAS = {
324
474
  # https://www.cosmos.esa.int/web/xmm-newton/technical-details-mirrors
325
475
  "xmm": 1500 * u.cm**2,
326
476
  "xmm slew": 1500 * u.cm**2,
477
+ "xmm-slew": 1500 * u.cm**2,
327
478
  "xmm pointed": 1500 * u.cm**2,
479
+ "xmm-newton": 1500 * u.cm**2,
328
480
  # https://cxc.harvard.edu/cdo/about_chandra
329
481
  "chandra": 600 * u.cm**2,
482
+ # https://www.cosmos.esa.int/documents/332006/954765/Brunner_TopicK.pdf
483
+ "erosita": 1500 * u.cm**2,
330
484
  }
331
485
  """
332
486
  X-Ray telescope areas that are used for converting from counts to other units.
@@ -338,6 +492,45 @@ NOTE: These are estimates from the following links
338
492
  * https://cxc.harvard.edu/cdo/about_chandra
339
493
  """
340
494
 
495
+ # Radio Band names with frequency ranges in GHz
496
+ RADIO_BAND_MAPPING = {
497
+ # use some lower bands from IEEE standards up to GMRT standards
498
+ # These are mostly here just as catchalls for low frequency radio telescopes
499
+ # like GEETEE or LOFAR
500
+ "HF": (0.003, 0.03),
501
+ "VHF": (0.03, 0.125), # Official: (0.03, 0.3) but adjust to work with GMRT standard
502
+ # use GMRT standards for low frequencies
503
+ "gmrt.2": (0.125, 0.250),
504
+ "gmrt.3": (0.25, 0.55), # Official: (0.25, 0.5)
505
+ # IEEE Standard Naming
506
+ # https://terasense.com/terahertz-technology/radio-frequency-bands/
507
+ "UHF": (0.55, 1),
508
+ "L": (1, 2),
509
+ "S": (2, 4),
510
+ "C": (4, 8),
511
+ "X": (8, 12),
512
+ "Ku": (12, 18),
513
+ "K": (18, 27),
514
+ "Ka": (27, 40),
515
+ # then switch to ALMA standard for mm
516
+ # https://www.eso.org/public/teles-instr/alma/receiver-bands/
517
+ # I widened some of the official ranges slightly so we don't leave gaps
518
+ # although these gaps probably wouldn't have mattered because **atmosphere**
519
+ "alma.1": (40, 50), # Official: (35, 50)
520
+ "alma.3": (84, 116), # skip alma.2 cause it's just a smaller range of alma.3
521
+ "alma.4": (116, 163), # official: (125, 163)
522
+ "alma.5": (163, 211),
523
+ "alma.6": (211, 275),
524
+ "alma.7": (275, 373),
525
+ "alma.8": (373, 500), # official: (385,500)
526
+ "alma.9": (500, 720), # official: (602, 720)
527
+ "alma.10": (787, 950),
528
+ }
529
+ """
530
+ Mapping of common radio/mm band names to their corresponding frequency ranges.
531
+ All frequencies are in GHz. The upperlimit is inclusive, that is these ranges
532
+ are really (xx, yy].
533
+ """
341
534
 
342
535
  # define a working base directory constant
343
536
  BASEDIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
@@ -345,11 +538,59 @@ BASEDIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file
345
538
  Base directory for the OTTER API software package
346
539
  """
347
540
 
541
+ VIZIER_LARGE_CATALOGS = [
542
+ "2MASS-PSC",
543
+ "2MASX",
544
+ "AC2000.2",
545
+ "AKARI",
546
+ "ALLWISE",
547
+ "ASCC-2.5",
548
+ "B/DENIS",
549
+ "CMC14",
550
+ "Gaia-DR1",
551
+ "GALEX",
552
+ "GLIMPSE",
553
+ "GSC-ACT",
554
+ "GSC1.2",
555
+ "GSC2.2",
556
+ "GSC2.3",
557
+ "HIP",
558
+ "HIP2",
559
+ "IRAS",
560
+ "NOMAD1",
561
+ "NVSS",
562
+ "PanSTARRS-DR1",
563
+ "PGC",
564
+ "Planck-DR1",
565
+ "PPMX",
566
+ "PPMXL",
567
+ "SDSS-DR12",
568
+ "SDSS-DR7",
569
+ "SDSS-DR9",
570
+ "Tycho-2",
571
+ "UCAC2",
572
+ "UCAC3",
573
+ "UCAC4",
574
+ "UKIDSS",
575
+ "USNO-A2",
576
+ "USNO-B1",
577
+ "WISE",
578
+ ]
579
+ """
580
+ ViZier catalog names that we query for host information in the Host class
581
+ """
582
+
348
583
  DATADIR = os.path.join(BASEDIR, "data", "base")
349
584
  """
350
585
  Deprecated database directory that IS NOT always constant anymore
351
586
  """
352
587
 
588
+ ####################################################################
589
+ # The following schema dictionaries are here so the code remains
590
+ # backwards compatible. BUT! All future code should use the pydantic
591
+ # schema defined in schema.py
592
+ ####################################################################
593
+
353
594
  # Overarching schema that stops once we get down to a string or list
354
595
  schema = {
355
596
  "schema_version": {"value": "0", "comment": "Copied from tde.space"},
@@ -567,45 +808,3 @@ subschema = {
567
808
  """
568
809
  A useful variable to describe all of the subschemas that are available and can be used
569
810
  """
570
-
571
- VIZIER_LARGE_CATALOGS = [
572
- "2MASS-PSC",
573
- "2MASX",
574
- "AC2000.2",
575
- "AKARI",
576
- "ALLWISE",
577
- "ASCC-2.5",
578
- "B/DENIS",
579
- "CMC14",
580
- "Gaia-DR1",
581
- "GALEX",
582
- "GLIMPSE",
583
- "GSC-ACT",
584
- "GSC1.2",
585
- "GSC2.2",
586
- "GSC2.3",
587
- "HIP",
588
- "HIP2",
589
- "IRAS",
590
- "NOMAD1",
591
- "NVSS",
592
- "PanSTARRS-DR1",
593
- "PGC",
594
- "Planck-DR1",
595
- "PPMX",
596
- "PPMXL",
597
- "SDSS-DR12",
598
- "SDSS-DR7",
599
- "SDSS-DR9",
600
- "Tycho-2",
601
- "UCAC2",
602
- "UCAC3",
603
- "UCAC4",
604
- "UKIDSS",
605
- "USNO-A2",
606
- "USNO-B1",
607
- "WISE",
608
- ]
609
- """
610
- ViZier catalog names that we query for host information in the Host class
611
- """
@@ -1,17 +0,0 @@
1
- otter/__init__.py,sha256=ml7fLtTVno4fvpJvw91bAp8ff2nuRG1hZ1lMCt3ZumY,440
2
- otter/_version.py,sha256=IwGQL03Wwse3OmpK33wS6CFwrF8rqxvbN79PydSE480,76
3
- otter/exceptions.py,sha256=3lQF4AXVTfs9VRsVePQoIrXnramsPZbUL5crvf1s9Ng,1702
4
- otter/util.py,sha256=7ZJYeTjBmwpXKgqXcmBtxhtn2j7XSQMaBbTurPKyEO0,15788
5
- otter/io/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
- otter/io/data_finder.py,sha256=pBGtfzjFt9pbJQR4exXP17eZoHVHClm_Jno1vXmcpAU,28026
7
- otter/io/host.py,sha256=ZYewz347_6FD5mVcQLDxHSS75n5RCv69xxLHAmpjoiI,4410
8
- otter/io/otter.py,sha256=VMtDCfiQniQNLIOL_P3NySwS-YnDF-nT4ceQjV0XJfE,18016
9
- otter/io/transient.py,sha256=YVOppPAqhChPx8sxRPfuC_9BBJVtE7ku5ITKkWKSL9w,35551
10
- otter/plotter/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
- otter/plotter/otter_plotter.py,sha256=iQe5AFsB5plw5eClUfuEfqcNRiXEcKc5-hkLPVKCkg8,2091
12
- otter/plotter/plotter.py,sha256=i9ZAlbIjftpzyFzgCIFdye9XnppTwOSwazb5ruLpjnE,2896
13
- astro_otter-0.1.0.dist-info/LICENSE,sha256=s9IPE8A3CAMEaZpDhj4eaorpmfLYGB0mIGphq301PUY,1067
14
- astro_otter-0.1.0.dist-info/METADATA,sha256=nR8WxIF2Yez-yZxyv0kMZ-QZHuJJdcClr--J9cvbqHw,5905
15
- astro_otter-0.1.0.dist-info/WHEEL,sha256=Z4pYXqR_rTB7OWNDYFOm1qRk0RX6GFP2o8LgvP453Hk,91
16
- astro_otter-0.1.0.dist-info/top_level.txt,sha256=Wth72sCwBRUk3KZGknSKvLQDMFuJk6qiaAavMDOdG5k,6
17
- astro_otter-0.1.0.dist-info/RECORD,,