astro-otter 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 astro-otter might be problematic. Click here for more details.

@@ -0,0 +1,866 @@
1
+ Metadata-Version: 2.1
2
+ Name: astro-otter
3
+ Version: 0.0.1
4
+ Author-email: Noah Franz <nfranz@arizona.edu>
5
+ License: MIT License
6
+
7
+ Copyright (c) 2023 Noah Franz
8
+
9
+ Permission is hereby granted, free of charge, to any person obtaining a copy
10
+ of this software and associated documentation files (the "Software"), to deal
11
+ in the Software without restriction, including without limitation the rights
12
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13
+ copies of the Software, and to permit persons to whom the Software is
14
+ furnished to do so, subject to the following conditions:
15
+
16
+ The above copyright notice and this permission notice shall be included in all
17
+ copies or substantial portions of the Software.
18
+
19
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25
+ SOFTWARE.
26
+
27
+ Project-URL: Home, https://github.com/astro-otter
28
+ Classifier: License :: OSI Approved :: BSD License
29
+ Classifier: Topic :: Scientific/Engineering
30
+ Classifier: Intended Audience :: Science/Research
31
+ Classifier: Intended Audience :: Developers
32
+ Classifier: Operating System :: OS Independent
33
+ Classifier: License :: OSI Approved :: BSD License
34
+ Classifier: Programming Language :: Python
35
+ Classifier: Programming Language :: Python :: 3
36
+ Classifier: Programming Language :: Python :: 3.9
37
+ Classifier: Programming Language :: Python :: 3.10
38
+ Classifier: Programming Language :: Python :: 3.11
39
+ Classifier: Development Status :: 2 - Pre-Alpha
40
+ Requires-Python: >=3.9
41
+ Description-Content-Type: text/markdown
42
+ License-File: LICENSE
43
+ Requires-Dist: numpy >=1.20
44
+ Requires-Dist: astropy >=5.2
45
+ Requires-Dist: pandas
46
+ Requires-Dist: matplotlib
47
+ Requires-Dist: plotly
48
+ Requires-Dist: astroquery
49
+ Requires-Dist: synphot
50
+ Requires-Dist: ads
51
+ Requires-Dist: ruff
52
+ Requires-Dist: pre-commit
53
+
54
+ # OTTER API
55
+ ### **O**pen mul**T**iwavelength **T**ransient **E**vent **R**epository
56
+
57
+ A Python API for the OTTER.
58
+
59
+ [actions-badge]: https://github.com/astro-otter/otter/workflows/CI/badge.svg
60
+ [actions-link]: https://github.com/astro-otter/otter/actions
61
+ [black-badge]: https://img.shields.io/badge/code%20style-black-000000.svg
62
+ [black-link]: https://github.com/psf/black
63
+ [conda-badge]: https://img.shields.io/conda/vn/conda-forge/hepfile
64
+ [conda-link]: https://github.com/conda-forge/hepfile-feedstock
65
+ [github-discussions-badge]: https://img.shields.io/static/v1?label=Discussions&message=Ask&color=blue&logo=github
66
+ [github-discussions-link]: https://github.com/mattbellis/hepfile/discussions
67
+ [gitter-badge]: https://badges.gitter.im/https://github.com/mattbellis/hepfile/community.svg
68
+ [gitter-link]: https://gitter.im/https://github.com/mattbellis/hepfile/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge
69
+ [pypi-link]: https://pypi.org/project/hepfile/
70
+ [pypi-platforms]: https://img.shields.io/pypi/pyversions/hepfile
71
+ [pypi-version]: https://badge.fury.io/py/hepfile.svg
72
+ [rtd-badge]: https://readthedocs.org/projects/otter/badge/?version=latest
73
+ [rtd-link]: https://otter.readthedocs.io/en/latest/?badge=latest
74
+ [sk-badge]: https://scikit-hep.org/assets/images/Scikit--HEP-Project-blue.svg
75
+ [ruff-badge]: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/charliermarsh/ruff/main/assets/badge/v2.json
76
+ [ruff-link]: https://github.com/astral-sh/ruff
77
+ [codecov-badge]: https://codecov.io/gh/astro-otter/otter/graph/badge.svg?token=BtCerOdTc0
78
+ [codecov-link]: https://codecov.io/gh/astro-otter/otter
79
+
80
+ [![Actions Status][actions-badge]][actions-link]
81
+ [![Linting: Ruff][ruff-badge]][ruff-link]
82
+ [![codecov][codecov-badge]][codecov-link]
83
+
84
+ ## Installation
85
+ To install the OTTER API use
86
+ ```
87
+ git clone https://github.com/astro-otter/otter.git
88
+ cd otter
89
+ python -m pip install .
90
+ ```
91
+ This will be changed into the more convenient `python -m pip install astro-otter` at a later date!
92
+
93
+ For developers, please also enable the pre-commit hooks using
94
+ ```
95
+ pre-commit install
96
+ ```
97
+
98
+ ## Tutorial
99
+ ### Connecting to the OTTER
100
+ ```python
101
+ # import the API
102
+ from otter import Otter, Transient
103
+ ```
104
+
105
+
106
+ ```python
107
+ # connect to the database
108
+ # this username and password is just for now and will be updated later!
109
+ db = Otter(username='user@otter', password='insecure')
110
+ ```
111
+
112
+ ### A typical workflow
113
+
114
+ First use `Otter.getMeta` to query
115
+
116
+ ```python
117
+ # can query by ANY name associated with an object
118
+ db.getMeta(names=['ASASSN-15oi', 'AT2020opy'])
119
+ ```
120
+
121
+
122
+
123
+
124
+ [{'name': {'default_name': 'ASASSN-15oi', 'alias': [{'value': 'ASASSN-15oi', 'reference': 'ASASSN'}]}, 'coordinate': {'equitorial': [{'ra': '20 39 09.096', 'dec': '-30 45 20.71', 'epoch': 'J2000', 'system': 'ICRS', 'ra_units': 'hourangle', 'dec_units': 'deg', 'reference': ['2021NatAs...5..491H'], 'computed': False, 'default': True, 'uuid': 'a06be641-1601-4737-9a1a-bd25c5dd61e6'}], 'galactic': [{'l': 13.01154485751856, 'b': -35.41877256185317, 'l_units': 'deg', 'b_units': 'deg', 'reference': 'a06be641-1601-4737-9a1a-bd25c5dd61e6', 'computed': True}]}, 'epoch': {'date_discovery': [{'value': 57248.2, 'date_format': 'MJD', 'reference': ['2021NatAs...5..491H'], 'computed': False}]}, 'distance': {'redshift': [{'value': '0.0484', 'reference': ['2021NatAs...5..491H'], 'computed': False, 'default': True}]}, 'classification': [{'object_class': 'TDE', 'confidence': 1, 'reference': ['2021NatAs...5..491H'], 'default': True}]},
125
+ {'name': {'default_name': 'AT2020opy', 'alias': [{'value': 'AT2020opy', 'reference': 'TNS'}]}, 'coordinate': {'equitorial': [{'ra': '15 56 25.728', 'dec': '+23 22 21.15', 'epoch': 'J2000', 'system': 'ICRS', 'ra_units': 'hourangle', 'dec_units': 'deg', 'reference': ['2023MNRAS.518..847G'], 'computed': False, 'default': True, 'uuid': '4f414ede-e0f0-4423-b2cc-f3ad309f0936'}], 'galactic': [{'l': 38.411569358993255, 'b': 48.23253616380999, 'l_units': 'deg', 'b_units': 'deg', 'reference': '4f414ede-e0f0-4423-b2cc-f3ad309f0936', 'computed': True}]}, 'epoch': {'date_discovery': [{'value': 59038.23, 'date_format': 'MJD', 'reference': ['2023MNRAS.518..847G'], 'computed': False}]}, 'distance': {'redshift': [{'value': '0.159', 'reference': ['2023MNRAS.518..847G'], 'computed': False, 'default': True}]}, 'classification': [{'object_class': 'TDE', 'confidence': 1, 'reference': ['2023MNRAS.518..847G'], 'default': True}]}]
126
+
127
+
128
+
129
+ We can also do a cone search
130
+ ```python
131
+ from astropy.coordinates import SkyCoord
132
+ import astropy.units as u
133
+ coord = SkyCoord(239, 23, unit=('deg', 'deg'))
134
+ rad = (1*u.deg).to(u.arcsec).value
135
+ db.getMeta(coords=coord, radius=rad)
136
+ ```
137
+
138
+
139
+
140
+
141
+ [{'name': {'default_name': 'AT2020opy', 'alias': [{'value': 'AT2020opy', 'reference': 'TNS'}]}, 'coordinate': {'equitorial': [{'ra': '15 56 25.728', 'dec': '+23 22 21.15', 'epoch': 'J2000', 'system': 'ICRS', 'ra_units': 'hourangle', 'dec_units': 'deg', 'reference': ['2023MNRAS.518..847G'], 'computed': False, 'default': True, 'uuid': '4f414ede-e0f0-4423-b2cc-f3ad309f0936'}], 'galactic': [{'l': 38.411569358993255, 'b': 48.23253616380999, 'l_units': 'deg', 'b_units': 'deg', 'reference': '4f414ede-e0f0-4423-b2cc-f3ad309f0936', 'computed': True}]}, 'epoch': {'date_discovery': [{'value': 59038.23, 'date_format': 'MJD', 'reference': ['2023MNRAS.518..847G'], 'computed': False}]}, 'distance': {'redshift': [{'value': '0.159', 'reference': ['2023MNRAS.518..847G'], 'computed': False, 'default': True}]}, 'classification': [{'object_class': 'TDE', 'confidence': 1, 'reference': ['2023MNRAS.518..847G'], 'default': True}]}]
142
+
143
+
144
+
145
+ Or search within a redshift range (or just a maximum or minimum)
146
+ ```python
147
+ # can search a redshift range
148
+ db.getMeta(minZ=0.5, maxZ=0.9)
149
+ ```
150
+
151
+
152
+
153
+
154
+ [{'name': {'default_name': 'Sw J1112-82', 'alias': [{'value': 'Sw J1112-82', 'reference': 'Swift'}]}, 'coordinate': {'equitorial': [{'ra': '11 11 47.6', 'dec': '-82 38 44.44', 'epoch': 'J2000', 'system': 'ICRS', 'ra_units': 'hourangle', 'dec_units': 'deg', 'reference': ['2017MNRAS.472.4469B'], 'computed': False, 'default': True, 'uuid': '3bb02aa4-eb60-4f09-acdf-d1431a6addc4'}], 'galactic': [{'l': 299.6337165869647, 'b': -20.420594756871665, 'l_units': 'deg', 'b_units': 'deg', 'reference': '3bb02aa4-eb60-4f09-acdf-d1431a6addc4', 'computed': True}]}, 'epoch': {'date_discovery': [{'value': '55729.5', 'date_format': 'MJD', 'reference': ['2017MNRAS.472.4469B'], 'computed': False}]}, 'distance': {'redshift': [{'value': '0.89', 'reference': ['2017MNRAS.472.4469B'], 'computed': False, 'default': True}]}, 'classification': [{'object_class': 'TDE', 'confidence': 1, 'reference': ['2017MNRAS.472.4469B'], 'default': True}]}]
155
+
156
+
157
+
158
+ We can even get all objects that have spectra associated with them
159
+ ```python
160
+ # just get objects that have spectra associated with them
161
+ db.getMeta(hasSpec=True)
162
+ ```
163
+
164
+
165
+
166
+
167
+ []
168
+
169
+ This should be empty because at the time of developing this tutorial there were no spectra in
170
+ OTTER. Similarly, we can get all objects that have photometry associated with them with
171
+ `db.getMeta(hasPhot=True)`.
172
+
173
+ These outputs may appear like dictionaries but they're actually customized!
174
+
175
+ Besides the typical dictionary methods the following methods are also implemented for Transient objects.
176
+
177
+ ```python
178
+ help(Transient)
179
+ ```
180
+
181
+ Help on class Transient in module otter.transient:
182
+
183
+ class Transient(collections.abc.MutableMapping)
184
+ | Transient(d={}, name=None)
185
+ |
186
+ | Method resolution order:
187
+ | Transient
188
+ | collections.abc.MutableMapping
189
+ | collections.abc.Mapping
190
+ | collections.abc.Collection
191
+ | collections.abc.Sized
192
+ | collections.abc.Iterable
193
+ | collections.abc.Container
194
+ | builtins.object
195
+ |
196
+ | Methods defined here:
197
+ |
198
+ | __add__(self, other, strict_merge=True)
199
+ | Merge this transient object with another transient object
200
+ |
201
+ | Args:
202
+ | other [Transient]: A Transient object to merge with
203
+ | strict_merge [bool]: If True it won't let you merge objects that
204
+ | intuitively shouldn't be merged (ie. different
205
+ | transient events).
206
+ |
207
+ | __delitem__(self, keys)
208
+ |
209
+ | __getitem__(self, keys)
210
+ |
211
+ | __init__(self, d={}, name=None)
212
+ | Overwrite the dictionary init
213
+ |
214
+ | Args:
215
+ | d [dict]: A transient dictionary
216
+ |
217
+ | __iter__(self)
218
+ |
219
+ | __len__(self)
220
+ |
221
+ | __repr__(self, html=False)
222
+ | Return repr(self).
223
+ |
224
+ | __setitem__(self, key, value)
225
+ |
226
+ | cleanPhotometry(self, flux_unit='mag(AB)', date_unit='MJD')
227
+ | Ensure the photometry associated with this transient is all in the same units/system/etc
228
+ |
229
+ | getMeta(self, keys=None)
230
+ | Get the metadata (no photometry or spectra)
231
+ |
232
+ | This essentially just wraps on __getitem__ but with some checks
233
+ |
234
+ | Args:
235
+ | keys [list[str]] : list of keys
236
+ |
237
+ | getSkyCoord(self, coord_type='equitorial', idx=0)
238
+ | Convert the coordinates to an astropy SkyCoord
239
+ |
240
+ | keys(self)
241
+ | D.keys() -> a set-like object providing a view on D's keys
242
+ |
243
+ | plotPhotometry(self, flux_unit='mag(AB)', date_unit='datetime', **kwargs)
244
+ | Plot the photometry associated with this transient (if any)
245
+ |
246
+ | Args:
247
+ | flux_unit [str]: Valid astropy unit string for the flux (y-axis) units.
248
+ | Default: 'ABmag'
249
+ | date_unit [str]: Valid astropy unit string for the date (x-axis) units.
250
+ | Default: 'MJD'
251
+
252
+
253
+ Some other advantages of the Transient objects are
254
+ ```python
255
+ t = db.getMeta(minZ=0.5, maxZ=0.9)[0]
256
+ print(type(t))
257
+ print()
258
+ # say you want to get the equitorial coordinates
259
+ # you can do it classically
260
+ print(t['coordinate']['equitorial'])
261
+
262
+ # or you can use the hdf5 style
263
+ print(t['coordinate/equitorial'])
264
+ ```
265
+
266
+ <class 'otter.transient.Transient'>
267
+
268
+ [{'ra': '11 11 47.6', 'dec': '-82 38 44.44', 'epoch': 'J2000', 'system': 'ICRS', 'ra_units': 'hourangle', 'dec_units': 'deg', 'reference': ['2017MNRAS.472.4469B'], 'computed': False, 'default': True, 'uuid': '3bb02aa4-eb60-4f09-acdf-d1431a6addc4'}]
269
+ [{'ra': '11 11 47.6', 'dec': '-82 38 44.44', 'epoch': 'J2000', 'system': 'ICRS', 'ra_units': 'hourangle', 'dec_units': 'deg', 'reference': ['2017MNRAS.472.4469B'], 'computed': False, 'default': True, 'uuid': '3bb02aa4-eb60-4f09-acdf-d1431a6addc4'}]
270
+
271
+
272
+ You can even get multiple fields at once like with astropy Tables or pandas DataFrames
273
+ ```python
274
+ # You can also get multiple fields at once
275
+ t[['name/default_name', 'coordinate/equitorial', 'distance']]
276
+ ```
277
+
278
+
279
+
280
+
281
+ {'name/default_name': 'Sw J1112-82', 'coordinate/equitorial': [{'ra': '11 11 47.6', 'dec': '-82 38 44.44', 'epoch': 'J2000', 'system': 'ICRS', 'ra_units': 'hourangle', 'dec_units': 'deg', 'reference': ['2017MNRAS.472.4469B'], 'computed': False, 'default': True, 'uuid': '3bb02aa4-eb60-4f09-acdf-d1431a6addc4'}], 'distance': {'redshift': [{'value': '0.89', 'reference': ['2017MNRAS.472.4469B'], 'computed': False, 'default': True}]}}
282
+
283
+
284
+
285
+ You can also add two Transient objects to merge them
286
+ ```python
287
+ t1, t2 = db.query(names=['ASASSN-15oi', 'AT2020opy'])
288
+
289
+ try:
290
+ t1 + t2
291
+ except ValueError as ve:
292
+ print('The following error is actually expected!')
293
+ print('We dont want you to be able to combine any old transients!')
294
+ print()
295
+ print('Error Message:')
296
+ print(ve)
297
+ ```
298
+
299
+ The following error is actually expected!
300
+ We dont want you to be able to combine any old transients!
301
+
302
+ Error Message:
303
+ These two transients are not within 5 arcseconds! They probably do not belong together! If they do You can set strict_merge=False to override the check
304
+
305
+
306
+ This error message is expected! If you want to override it then you can do
307
+ ```python
308
+ t2['photometry'][0]['reference'] = '2021NatAs...5..491H'
309
+
310
+ t3 = t1.__add__(t2, strict_merge=False)
311
+ ```
312
+
313
+ Obviously, this result doesn't makes sense! This has the data from two completely different transients in it. So, be careful using `strict_merge=False`!
314
+
315
+ ### Can then get photometry
316
+ This does the conversion for you!!!
317
+
318
+
319
+ ```python
320
+ db.getPhot?
321
+ ```
322
+ Get the photometry of the objects matching the arguments. This will do the
323
+ unit conversion for you!
324
+
325
+ Args:
326
+ flux_units [astropy.unit.Unit]: Either a valid string to convert
327
+ or an astropy.unit.Unit
328
+ date_units [astropy.unit.Unit]: Either a valid string to convert to a date
329
+ or an astropy.unit.Unit
330
+ return_type [str]: Either 'astropy' or 'pandas'. If astropy, returns an
331
+ astropy Table. If pandas, returns a pandas DataFrame.
332
+ Default is 'astropy'.
333
+
334
+ **kwargs : Arguments to pass to Otter.query(). Can be:
335
+ names [list[str]]: A list of names to get the metadata for
336
+ coords [SkyCoord]: An astropy SkyCoord object with coordinates to match to
337
+ radius [float]: The radius in arcseconds for a cone search, default is 0.05"
338
+ minZ [float]: The minimum redshift to search for
339
+ maxZ [float]: The maximum redshift to search for
340
+ refs [list[str]]: A list of ads bibcodes to match to. Will only return
341
+ metadata for transients that have this as a reference.
342
+ hasSpec [bool]: if True, only return transients that have spectra.
343
+
344
+ Return:
345
+ The photometry for the requested transients that match the arguments.
346
+ Will be an astropy Table sorted by transient default name.
347
+
348
+
349
+ This means you can easily grab photometry in consistent units to plot!
350
+ ```python
351
+ import matplotlib.pyplot as plt
352
+ flux_unit = u.ABmag #u.erg/u.s/u.cm**2/u.Hz #'erg/s/cm^2/Hz'
353
+ tab = db.getPhot(flux_unit=flux_unit, date_unit='datetime', names=['ASASSN-15oi', 'ASASSN-14li'], return_type='pandas')
354
+
355
+ tab
356
+ ```
357
+
358
+
359
+
360
+
361
+ <div>
362
+ <table border="1" class="dataframe">
363
+ <thead>
364
+ <tr style="text-align: right;">
365
+ <th></th>
366
+ <th>reference</th>
367
+ <th>date</th>
368
+ <th>filter_key</th>
369
+ <th>computed</th>
370
+ <th>obs_type</th>
371
+ <th>upperlimit</th>
372
+ <th>freq_eff</th>
373
+ <th>freq_units</th>
374
+ <th>human_readable_refs</th>
375
+ <th>converted_flux</th>
376
+ <th>converted_date</th>
377
+ <th>name</th>
378
+ </tr>
379
+ </thead>
380
+ <tbody>
381
+ <tr>
382
+ <th>0</th>
383
+ <td>2016ApJ...819L..25A</td>
384
+ <td>57124.871000</td>
385
+ <td>5.0GHz</td>
386
+ <td>False</td>
387
+ <td>radio</td>
388
+ <td>False</td>
389
+ <td>5.0</td>
390
+ <td>GHz</td>
391
+ <td>Alexander et al. (2016)</td>
392
+ <td>15.697417</td>
393
+ <td>2015-04-12 20:54:14.400000</td>
394
+ <td>ASASSN-14li</td>
395
+ </tr>
396
+ <tr>
397
+ <th>1</th>
398
+ <td>2016ApJ...819L..25A</td>
399
+ <td>57190.830000</td>
400
+ <td>5.0GHz</td>
401
+ <td>False</td>
402
+ <td>radio</td>
403
+ <td>False</td>
404
+ <td>5.0</td>
405
+ <td>GHz</td>
406
+ <td>Alexander et al. (2016)</td>
407
+ <td>15.797567</td>
408
+ <td>2015-06-17 19:55:12.000000</td>
409
+ <td>ASASSN-14li</td>
410
+ </tr>
411
+ <tr>
412
+ <th>2</th>
413
+ <td>2016ApJ...819L..25A</td>
414
+ <td>57229.750000</td>
415
+ <td>5.0GHz</td>
416
+ <td>False</td>
417
+ <td>radio</td>
418
+ <td>False</td>
419
+ <td>5.0</td>
420
+ <td>GHz</td>
421
+ <td>Alexander et al. (2016)</td>
422
+ <td>15.915797</td>
423
+ <td>2015-07-26 18:00:00.000000</td>
424
+ <td>ASASSN-14li</td>
425
+ </tr>
426
+ <tr>
427
+ <th>3</th>
428
+ <td>2016ApJ...819L..25A</td>
429
+ <td>57286.514583</td>
430
+ <td>5.0GHz</td>
431
+ <td>False</td>
432
+ <td>radio</td>
433
+ <td>False</td>
434
+ <td>5.0</td>
435
+ <td>GHz</td>
436
+ <td>Alexander et al. (2016)</td>
437
+ <td>16.152094</td>
438
+ <td>2015-09-21 12:20:59.997120</td>
439
+ <td>ASASSN-14li</td>
440
+ </tr>
441
+ <tr>
442
+ <th>4</th>
443
+ <td>2016ApJ...819L..25A</td>
444
+ <td>57362.700000</td>
445
+ <td>5.0GHz</td>
446
+ <td>False</td>
447
+ <td>radio</td>
448
+ <td>False</td>
449
+ <td>5.0</td>
450
+ <td>GHz</td>
451
+ <td>Alexander et al. (2016)</td>
452
+ <td>16.547278</td>
453
+ <td>2015-12-06 16:48:00.000000</td>
454
+ <td>ASASSN-14li</td>
455
+ </tr>
456
+ <tr>
457
+ <th>0</th>
458
+ <td>2021NatAs...5..491H</td>
459
+ <td>57256.200000</td>
460
+ <td>6.1GHz</td>
461
+ <td>False</td>
462
+ <td>radio</td>
463
+ <td>True</td>
464
+ <td>6.1</td>
465
+ <td>GHz</td>
466
+ <td>Horesh et al. (2021)</td>
467
+ <td>20.103715</td>
468
+ <td>2015-08-22 04:48:00.000000</td>
469
+ <td>ASASSN-15oi</td>
470
+ </tr>
471
+ <tr>
472
+ <th>1</th>
473
+ <td>2021NatAs...5..491H</td>
474
+ <td>57271.200000</td>
475
+ <td>6.1GHz</td>
476
+ <td>False</td>
477
+ <td>radio</td>
478
+ <td>True</td>
479
+ <td>6.1</td>
480
+ <td>GHz</td>
481
+ <td>Horesh et al. (2021)</td>
482
+ <td>20.009244</td>
483
+ <td>2015-09-06 04:48:00.000000</td>
484
+ <td>ASASSN-15oi</td>
485
+ </tr>
486
+ <tr>
487
+ <th>2</th>
488
+ <td>2021NatAs...5..491H</td>
489
+ <td>57338.200000</td>
490
+ <td>6.1GHz</td>
491
+ <td>False</td>
492
+ <td>radio</td>
493
+ <td>True</td>
494
+ <td>6.1</td>
495
+ <td>GHz</td>
496
+ <td>Horesh et al. (2021)</td>
497
+ <td>19.454622</td>
498
+ <td>2015-11-12 04:48:00.000000</td>
499
+ <td>ASASSN-15oi</td>
500
+ </tr>
501
+ <tr>
502
+ <th>3</th>
503
+ <td>2021NatAs...5..491H</td>
504
+ <td>57430.200000</td>
505
+ <td>4.8GHz</td>
506
+ <td>False</td>
507
+ <td>radio</td>
508
+ <td>False</td>
509
+ <td>4.8</td>
510
+ <td>GHz</td>
511
+ <td>Horesh et al. (2021)</td>
512
+ <td>16.282787</td>
513
+ <td>2016-02-12 04:48:00.000000</td>
514
+ <td>ASASSN-15oi</td>
515
+ </tr>
516
+ <tr>
517
+ <th>4</th>
518
+ <td>2021NatAs...5..491H</td>
519
+ <td>57438.200000</td>
520
+ <td>4.8GHz</td>
521
+ <td>False</td>
522
+ <td>radio</td>
523
+ <td>False</td>
524
+ <td>4.8</td>
525
+ <td>GHz</td>
526
+ <td>Horesh et al. (2021)</td>
527
+ <td>16.515601</td>
528
+ <td>2016-02-20 04:48:00.000000</td>
529
+ <td>ASASSN-15oi</td>
530
+ </tr>
531
+ <tr>
532
+ <th>5</th>
533
+ <td>2021NatAs...5..491H</td>
534
+ <td>57445.200000</td>
535
+ <td>5.5GHz</td>
536
+ <td>False</td>
537
+ <td>radio</td>
538
+ <td>False</td>
539
+ <td>5.5</td>
540
+ <td>GHz</td>
541
+ <td>Horesh et al. (2021)</td>
542
+ <td>16.537560</td>
543
+ <td>2016-02-27 04:48:00.000000</td>
544
+ <td>ASASSN-15oi</td>
545
+ </tr>
546
+ <tr>
547
+ <th>6</th>
548
+ <td>2021NatAs...5..491H</td>
549
+ <td>57481.200000</td>
550
+ <td>5.5GHz</td>
551
+ <td>False</td>
552
+ <td>radio</td>
553
+ <td>False</td>
554
+ <td>5.5</td>
555
+ <td>GHz</td>
556
+ <td>Horesh et al. (2021)</td>
557
+ <td>16.610182</td>
558
+ <td>2016-04-03 04:48:00.000000</td>
559
+ <td>ASASSN-15oi</td>
560
+ </tr>
561
+ <tr>
562
+ <th>7</th>
563
+ <td>2021NatAs...5..491H</td>
564
+ <td>57531.200000</td>
565
+ <td>5.0GHz</td>
566
+ <td>False</td>
567
+ <td>radio</td>
568
+ <td>False</td>
569
+ <td>5.0</td>
570
+ <td>GHz</td>
571
+ <td>Horesh et al. (2021)</td>
572
+ <td>16.571355</td>
573
+ <td>2016-05-23 04:48:00.000000</td>
574
+ <td>ASASSN-15oi</td>
575
+ </tr>
576
+ <tr>
577
+ <th>8</th>
578
+ <td>2021NatAs...5..491H</td>
579
+ <td>57617.200000</td>
580
+ <td>5.0GHz</td>
581
+ <td>False</td>
582
+ <td>radio</td>
583
+ <td>False</td>
584
+ <td>5.0</td>
585
+ <td>GHz</td>
586
+ <td>Horesh et al. (2021)</td>
587
+ <td>16.924287</td>
588
+ <td>2016-08-17 04:48:00.000000</td>
589
+ <td>ASASSN-15oi</td>
590
+ </tr>
591
+ <tr>
592
+ <th>9</th>
593
+ <td>2021NatAs...5..491H</td>
594
+ <td>57824.200000</td>
595
+ <td>5.0GHz</td>
596
+ <td>False</td>
597
+ <td>radio</td>
598
+ <td>False</td>
599
+ <td>5.0</td>
600
+ <td>GHz</td>
601
+ <td>Horesh et al. (2021)</td>
602
+ <td>17.854247</td>
603
+ <td>2017-03-12 04:48:00.000000</td>
604
+ <td>ASASSN-15oi</td>
605
+ </tr>
606
+ <tr>
607
+ <th>10</th>
608
+ <td>2021NatAs...5..491H</td>
609
+ <td>58665.200000</td>
610
+ <td>3.0GHz</td>
611
+ <td>False</td>
612
+ <td>radio</td>
613
+ <td>False</td>
614
+ <td>3.0</td>
615
+ <td>GHz</td>
616
+ <td>Horesh et al. (2021)</td>
617
+ <td>14.142275</td>
618
+ <td>2019-07-01 04:48:00.000000</td>
619
+ <td>ASASSN-15oi</td>
620
+ </tr>
621
+ </tbody>
622
+ </table>
623
+ </div>
624
+
625
+
626
+
627
+
628
+ ```python
629
+ fig, ax = plt.subplots()
630
+ for key, table in tab.groupby('name'):
631
+ ax.plot(table['converted_date'], table['converted_flux'], label=key, marker='o', linestyle='none')
632
+
633
+ ax.set_ylabel(f'Flux Density [{flux_unit}]')
634
+ ax.set_xlabel('Date')
635
+ ax.set_xticks(ax.get_xticks(), ax.get_xticklabels(), rotation=45)
636
+ ax.legend();
637
+ ```
638
+
639
+
640
+ ![png](api-test_files/api-test_17_0.png)
641
+
642
+
643
+ ### General Queries (shouldn't be used unless you know what you're doing!)
644
+
645
+ The structure of a more generalized query to OTTER is
646
+ ```python
647
+ # General queries
648
+ Otter.query?
649
+ ```
650
+ Wraps on the super.AQLQuery and queries the OTTER database more intuitively.
651
+
652
+ WARNING! This does not do any conversions for you!
653
+ This is how it differs from the `getMeta` method. Users should prefer to use
654
+ `getMeta`, `getPhot`, and `getSpec` independently because it is a better
655
+ workflow and can return the data in an astropy table with everything in the
656
+ same units.
657
+
658
+ Args:
659
+ names [list[str]]: A list of names to get the metadata for
660
+ coords [SkyCoord]: An astropy SkyCoord object with coordinates to match to
661
+ radius [float]: The radius in arcseconds for a cone search, default is 0.05"
662
+ minZ [float]: The minimum redshift to search for
663
+ maxZ [float]: The maximum redshift to search for
664
+ refs [list[str]]: A list of ads bibcodes to match to. Will only return
665
+ metadata for transients that have this as a reference.
666
+ hasPhot [bool]: if True, only returns transients which have photometry.
667
+ hasSpec [bool]: if True, only return transients that have spectra.
668
+
669
+ Return:
670
+ Get all of the raw (unconverted!) json data for objects that match the criteria.
671
+
672
+
673
+ An example of this is
674
+ ```python
675
+ res = db.query(names=['ASASSN-15oi', 'AT2020opy'])
676
+ print(len(res))
677
+ print(type(res[0]))
678
+ ```
679
+
680
+ 2
681
+ <class 'otter.transient.Transient'>
682
+
683
+
684
+
685
+ ```python
686
+ res[0].keys()
687
+ ```
688
+
689
+
690
+
691
+
692
+ dict_keys(['_key', '_id', '_rev', 'schema_version', 'name', 'coordinate', 'distance', 'classification', 'reference_alias', 'epoch', 'photometry', 'filter_alias'])
693
+
694
+
695
+
696
+ However, Notice how this is simply the raw results!!! This means you need to be very careful with the results you get from these queries because no conversion is done!
697
+ ```python
698
+ res[0]
699
+ ```
700
+
701
+
702
+
703
+
704
+ {'_key': '3776907', '_id': 'tdes/3776907', '_rev': '_hD8NTy2---', 'schema_version': {'value': '0', 'comment': 'Original Dataset'}, 'name': {'default_name': 'ASASSN-15oi', 'alias': [{'value': 'ASASSN-15oi', 'reference': 'ASASSN'}]}, 'coordinate': {'equitorial': [{'ra': '20 39 09.096', 'dec': '-30 45 20.71', 'epoch': 'J2000', 'system': 'ICRS', 'ra_units': 'hourangle', 'dec_units': 'deg', 'reference': ['2021NatAs...5..491H'], 'computed': False, 'default': True, 'uuid': 'a06be641-1601-4737-9a1a-bd25c5dd61e6'}], 'galactic': [{'l': 13.01154485751856, 'b': -35.41877256185317, 'l_units': 'deg', 'b_units': 'deg', 'reference': 'a06be641-1601-4737-9a1a-bd25c5dd61e6', 'computed': True}]}, 'distance': {'redshift': [{'value': '0.0484', 'reference': ['2021NatAs...5..491H'], 'computed': False, 'default': True}]}, 'classification': [{'object_class': 'TDE', 'confidence': 1, 'reference': ['2021NatAs...5..491H'], 'default': True}], 'reference_alias': [{'name': '2021NatAs...5..491H', 'human_readable_name': 'Horesh et al. (2021)'}], 'epoch': {'date_discovery': [{'value': 57248.2, 'date_format': 'MJD', 'reference': ['2021NatAs...5..491H'], 'computed': False}]}, 'photometry': [{'reference': '2021NatAs...5..491H', 'raw': [0.033, 0.036, 0.06, 1.114, 0.899, 0.881, 0.824, 0.854, 0.617, 0.262, 8], 'raw_units': ['mJy', 'mJy', 'mJy', 'mJy', 'mJy', 'mJy', 'mJy', 'mJy', 'mJy', 'mJy', 'mJy'], 'date': [57256.2, 57271.2, 57338.2, 57430.2, 57438.2, 57445.2, 57481.2, 57531.2, 57617.2, 57824.2, 58665.2], 'date_format': ['MJD', 'MJD', 'MJD', 'MJD', 'MJD', 'MJD', 'MJD', 'MJD', 'MJD', 'MJD', 'MJD'], 'filter_key': ['6.1GHz', '6.1GHz', '6.1GHz', '4.8GHz', '4.8GHz', '5.5GHz', '5.5GHz', '5.0GHz', '5.0GHz', '5.0GHz', '3.0GHz'], 'computed': [False, False, False, False, False, False, False, False, False, False, False], 'obs_type': ['radio', 'radio', 'radio', 'radio', 'radio', 'radio', 'radio', 'radio', 'radio', 'radio', 'radio'], 'upperlimit': [True, True, True, False, False, False, False, False, False, False, False]}], 'filter_alias': [{'filter_key': '6.1GHz', 'freq_eff': 6.1, 'freq_units': 'GHz'}, {'filter_key': '4.8GHz', 'freq_eff': 4.8, 'freq_units': 'GHz'}, {'filter_key': '5.5GHz', 'freq_eff': 5.5, 'freq_units': 'GHz'}, {'filter_key': '5.0GHz', 'freq_eff': 5, 'freq_units': 'GHz'}, {'filter_key': '3.0GHz', 'freq_eff': 3, 'freq_units': 'GHz'}]}
705
+
706
+
707
+
708
+ ### Some helpful methods
709
+
710
+ To get the Astropy SkyCoord object for a specific transient you can use
711
+ ```python
712
+ skycoord = t1.getSkyCoord()
713
+ skycoord
714
+ ```
715
+
716
+
717
+
718
+
719
+ <SkyCoord (ICRS): (ra, dec) in deg
720
+ (309.7879, -30.75575278)>
721
+
722
+
723
+
724
+ Or, to get the html code for plotting the photometry for a transient
725
+ ```python
726
+ html = res[0].plotPhotometry()
727
+ html[:100] # only show the first part of it
728
+ ```
729
+
730
+
731
+
732
+
733
+ '<div> <script type="text/javascript">window.PlotlyConfig = {MathJaxConfig: \'l'
734
+
735
+
736
+
737
+ ### Uploading/Editing Data
738
+ If you have admin access to OTTER you can also upload new data! An example of this is that we first
739
+ need to create a new Transient object.
740
+
741
+ ```python
742
+ from otter import Otter, Transient
743
+ from copy import deepcopy
744
+ from collections import Counter
745
+ import awkward as ak
746
+ import warnings
747
+ import numpy as np
748
+ import re
749
+ from astropy.coordinates import SkyCoord
750
+ import json
751
+
752
+ # generate some test cases
753
+ db = Otter()
754
+ t1 = db.query(names='2022xkq')[0] #
755
+ t2 = deepcopy(t1)
756
+ print(t1.keys())
757
+
758
+ # change t2 for testing
759
+ t2['name'] = {'default_name':'2022xkq',
760
+ 'alias': [{'value':'foo', 'reference': 'x'},
761
+ {'value': '2022xkq', 'reference': 'x'}]}
762
+ t2['reference_alias'].append({'name': 'x',
763
+ 'human_readable_name': 'test, name (year)'}) # add an extra value
764
+ del t2['photometry']
765
+ t2['for_test'] = {'test': 'bar'} # add a test key that isn't in t1
766
+ t2['coordinate/equitorial'][0]['reference'] = 'noah'
767
+ t2['filter_alias'].append({'filter_key': 'foo'})
768
+ t2['schema_version/value'] = 100
769
+ t2['epoch'] = {'date_peak': [{'value': 56983,
770
+ 'date_format': 'MJD',
771
+ 'reference': ['2016ApJ...819L..25A',
772
+ '2016Sci...351...62V',
773
+ '2016ApJ...832L..10R',
774
+ '2018MNRAS.475.4011B'],
775
+ 'computed': False}],
776
+
777
+ 'date_discovery': [{'value': 56983,
778
+ 'date_format': 'MJD',
779
+ 'reference': ['2016ApJ...819L..25A',
780
+ '2016Sci...351...62V',
781
+ '2016ApJ...832L..10R',
782
+ '2018MNRAS.475.4011B'],
783
+ 'computed': False}],
784
+
785
+
786
+ 'date_discovery': [{'value': 56984,
787
+ 'date_format': 'MJD',
788
+ 'reference': ['2016ApJ...819L..25A',
789
+ '2016Sci...351...62V',
790
+ '2016ApJ...832L..10R',
791
+ '2018MNRAS.475.4011B'],
792
+ 'computed': False}]
793
+ }
794
+
795
+ t2['distance'] = {
796
+ "redshift": [
797
+ {
798
+ "value": "0.0207",
799
+ "reference": [
800
+ "Noah"
801
+ ],
802
+ "computed": False
803
+ },
804
+ {
805
+ "value": "0.02",
806
+ "reference": [
807
+ "Noah"
808
+ ],
809
+ "computed": False
810
+ }
811
+ ],
812
+
813
+ "dispersion_measure": [
814
+ {
815
+ "value": "0.0206",
816
+ "reference": [
817
+ "Noah"
818
+ ],
819
+ "computed": False
820
+ }
821
+ ]
822
+ }
823
+
824
+ t2['classification'] = [{'object_class':'SN',
825
+ 'confidence': 1,
826
+ 'reference': 'Noah'
827
+ }]
828
+
829
+ t2['photometry'] = {'phot_0': {'telescope': 'Noahs Telescope',
830
+ 'reference': 'Noah',
831
+ 'flux': [{'filter': 'z',
832
+ 'telescope': 'Noahs Telescope',
833
+ 'upperlimit': True,
834
+ 'date': 59864.4914116667,
835
+ 'date_format': 'MJD',
836
+ 'raw': 20.01,
837
+ 'raw_units': 'mag(AB)',
838
+ 'filter_key': 'NoahsTelescope.z',
839
+ 'obs_type': 'uvoir'}]},
840
+ 'phot_1': {'telescope': 'CAHA',
841
+ 'reference': 'Noah',
842
+ 'flux': [{'filter': 'H',
843
+ 'telescope': 'CAHA',
844
+ 'upperlimit': False,
845
+ 'date': 59898.12077,
846
+ 'date_format': 'MJD',
847
+ 'raw': 14.87048,
848
+ 'raw_err': 0.0187,
849
+ 'raw_units': 'mag(AB)',
850
+ 'filter_key': 'CAHA.H',
851
+ 'obs_type': 'uvoir'}]}
852
+
853
+ }
854
+ ```
855
+
856
+ Now we need to connect to OTTER with admin access and upload the new transient object.
857
+ ```python
858
+ db = Otter(username='admin@otter', password='insecure')
859
+ #db.upload(t2)
860
+ ```
861
+
862
+
863
+ ## Repo Organization
864
+ | Directory | Contents |
865
+ |------------|------------|
866
+ | `py/otter` | A pip installable API for interfacing with the ArangoDB database|