astro-otter 0.0.1__py3-none-any.whl → 0.1.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.
- astro_otter-0.1.0.dist-info/METADATA +127 -0
- astro_otter-0.1.0.dist-info/RECORD +17 -0
- {astro_otter-0.0.1.dist-info → astro_otter-0.1.0.dist-info}/WHEEL +1 -1
- otter/__init__.py +4 -0
- otter/_version.py +1 -1
- otter/exceptions.py +48 -0
- otter/io/data_finder.py +744 -0
- otter/io/host.py +106 -0
- otter/io/otter.py +82 -56
- otter/io/transient.py +240 -147
- otter/plotter/otter_plotter.py +10 -8
- otter/plotter/plotter.py +19 -18
- otter/util.py +102 -1
- astro_otter-0.0.1.dist-info/METADATA +0 -866
- astro_otter-0.0.1.dist-info/RECORD +0 -15
- {astro_otter-0.0.1.dist-info → astro_otter-0.1.0.dist-info}/LICENSE +0 -0
- {astro_otter-0.0.1.dist-info → astro_otter-0.1.0.dist-info}/top_level.txt +0 -0
otter/io/host.py
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Host object that stores information on the Transient Host and provides utility methods
|
|
3
|
+
for pulling in data corresponding to that host
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
import numpy as np
|
|
8
|
+
from astropy.coordinates import SkyCoord
|
|
9
|
+
from astropy import units as u
|
|
10
|
+
|
|
11
|
+
from .data_finder import DataFinder
|
|
12
|
+
from ..exceptions import OtterLimitationError
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class Host(DataFinder):
|
|
16
|
+
def __init__(
|
|
17
|
+
self,
|
|
18
|
+
host_ra: str | float,
|
|
19
|
+
host_dec: str | float,
|
|
20
|
+
host_ra_units: str | u.Unit,
|
|
21
|
+
host_dec_units: str | u.Unit,
|
|
22
|
+
host_name: str = None,
|
|
23
|
+
host_redshift: float = None,
|
|
24
|
+
reference: list[str] = None,
|
|
25
|
+
transient_name: str = None,
|
|
26
|
+
**kwargs,
|
|
27
|
+
) -> None:
|
|
28
|
+
"""
|
|
29
|
+
Object to store host information and query public data sources of host galaxies
|
|
30
|
+
|
|
31
|
+
Subclass of the data scraper class to allow for these queries to happen
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
host_ra (str|float) : The RA of the host to be passed to an astropy SkyCoord
|
|
35
|
+
host_dec (str|float) : The declination of the host to be passed to an
|
|
36
|
+
astropy SkyCoord
|
|
37
|
+
host_ra_units (str|astropy.units.Unit) : units of the RA, to be passed to
|
|
38
|
+
the unit keyword of SkyCoord
|
|
39
|
+
host_dec_units (str|astropy.units.Unit) : units of the declination, to be
|
|
40
|
+
passed to the unit keyword of
|
|
41
|
+
SkyCoord
|
|
42
|
+
host_name (str) : The name of the host galaxy
|
|
43
|
+
host_redshift (float) : The redshift of the host galaxy
|
|
44
|
+
reference (list[str]) : a list of bibcodes that found this to be the host
|
|
45
|
+
transient_name (str) : the name of the transient associated with this host
|
|
46
|
+
kwargs : Just here so we can pass **Transient['host'] into this constructor
|
|
47
|
+
and any extraneous properties will be ignored.
|
|
48
|
+
"""
|
|
49
|
+
self.coord = SkyCoord(host_ra, host_dec, unit=(host_ra_units, host_dec_units))
|
|
50
|
+
self.name = host_name
|
|
51
|
+
self.z = host_redshift
|
|
52
|
+
self.redshift = host_redshift # just here for ease of use
|
|
53
|
+
self.bibcodes = reference
|
|
54
|
+
self.transient_name = transient_name
|
|
55
|
+
|
|
56
|
+
def pcc(self, transient_coord: SkyCoord, mag: float = None):
|
|
57
|
+
"""
|
|
58
|
+
Compute the Probability of Chance Coincindence as described in
|
|
59
|
+
Bloom et al. (2002) "Offset Distribution of Gamma-Ray Bursts.
|
|
60
|
+
|
|
61
|
+
This computes the probability that this galaxy is by chance nearby to the
|
|
62
|
+
transient on the sky. Or, in simpler terms this essentially computes the
|
|
63
|
+
probability that we are wrong about this being the transient host. So, a
|
|
64
|
+
smaller probability is better!
|
|
65
|
+
|
|
66
|
+
Note: This probability was initially defined for GRB afterglows, which tend to
|
|
67
|
+
be redder transients (supernova too). So, be cautious when using this algorithm
|
|
68
|
+
for TDEs!
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
transient_coord (astropy.coordinates.SkyCoord) : The coordinates of the
|
|
72
|
+
transient object.
|
|
73
|
+
mag (float) : An r-band magnitude to compute from. Default is None which
|
|
74
|
+
will prompt us to check SDSS for one within 10".
|
|
75
|
+
Returns:
|
|
76
|
+
A float probability in the range [0,1]
|
|
77
|
+
"""
|
|
78
|
+
|
|
79
|
+
# first get the separation r, in arcseconds
|
|
80
|
+
r = self.coord.separation(transient_coord).arcsec
|
|
81
|
+
|
|
82
|
+
# next get the host r magnitude
|
|
83
|
+
if mag is None:
|
|
84
|
+
res = self.query_vizier(radius=10 * u.arcsec)
|
|
85
|
+
|
|
86
|
+
if len(res) == 0:
|
|
87
|
+
raise OtterLimitationError(
|
|
88
|
+
"No magnitude found in SDSS! Please provide a magnitude via the \
|
|
89
|
+
`mag` keyword to make this calculation!"
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
sdss = [k for k in res.keys() if "sdss" in k]
|
|
93
|
+
use = max(sdss, key=lambda k: int(k.split("sdss")[-1]))
|
|
94
|
+
print(f"Using the r magnitude from the {use} table")
|
|
95
|
+
mag = res[use]["rmag"][0]
|
|
96
|
+
|
|
97
|
+
# then compute the probability
|
|
98
|
+
sigma_prefactor = 1 / (3600**2 * 0.334 * np.log(10))
|
|
99
|
+
sigma_pow = 0.334 * (mag - 22.963) + 4.320
|
|
100
|
+
sigma = sigma_prefactor * 10**sigma_pow
|
|
101
|
+
|
|
102
|
+
eta = np.pi * r**2 * sigma
|
|
103
|
+
|
|
104
|
+
prob = 1 - np.exp(-eta)
|
|
105
|
+
|
|
106
|
+
return prob
|
otter/io/otter.py
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
This is the primary class for user interaction with the catalog
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
+
from __future__ import annotations
|
|
5
6
|
import os
|
|
6
7
|
import json
|
|
7
8
|
import glob
|
|
@@ -28,14 +29,11 @@ class Otter(object):
|
|
|
28
29
|
This is the primary class for users to access the otter backend database
|
|
29
30
|
|
|
30
31
|
Args:
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
collection [str]: The collection to read data from. Right now the only
|
|
37
|
-
collection is 'tdes'.
|
|
38
|
-
debug [bool]: debug mode, set to true to limit reading from database.
|
|
32
|
+
datadir (str): Path to the data directory with the otter data. If not provided
|
|
33
|
+
will default to a ".otter" directory in the CWD where you call
|
|
34
|
+
this class from.
|
|
35
|
+
debug (bool): If we should just debug and not do anything serious.
|
|
36
|
+
|
|
39
37
|
"""
|
|
40
38
|
|
|
41
39
|
def __init__(self, datadir: str = None, debug: bool = False) -> None:
|
|
@@ -87,9 +85,9 @@ class Otter(object):
|
|
|
87
85
|
Performs a cone search of the catalog over the given coords and radius.
|
|
88
86
|
|
|
89
87
|
Args:
|
|
90
|
-
coords
|
|
91
|
-
radius
|
|
92
|
-
raw
|
|
88
|
+
coords (SkyCoord): An astropy SkyCoord object with coordinates to match to
|
|
89
|
+
radius (float): The radius of the cone in arcseconds, default is 0.05"
|
|
90
|
+
raw (bool): If False (the default) return an astropy table of the metadata
|
|
93
91
|
for matching objects. Otherwise, return the raw json dicts
|
|
94
92
|
|
|
95
93
|
Return:
|
|
@@ -117,35 +115,40 @@ class Otter(object):
|
|
|
117
115
|
unit conversion for you!
|
|
118
116
|
|
|
119
117
|
Args:
|
|
120
|
-
flux_units
|
|
118
|
+
flux_units (astropy.unit.Unit): Either a valid string to convert
|
|
121
119
|
or an astropy.unit.Unit
|
|
122
|
-
date_units
|
|
120
|
+
date_units (astropy.unit.Unit): Either a valid string to convert to a date
|
|
123
121
|
or an astropy.unit.Unit
|
|
124
|
-
return_type
|
|
122
|
+
return_type (str): Either 'astropy' or 'pandas'. If astropy, returns an
|
|
125
123
|
astropy Table. If pandas, returns a pandas DataFrame.
|
|
126
124
|
Default is 'astropy'.
|
|
127
|
-
obs_type
|
|
125
|
+
obs_type (str): Either 'radio', 'uvoir', or 'xray'. Will only return that
|
|
128
126
|
type of photometry if not None. Default is None and will
|
|
129
127
|
return any type of photometry.
|
|
130
|
-
keep_raw
|
|
128
|
+
keep_raw (bool): If True, keep the raw flux/date/freq/wave associated with
|
|
131
129
|
the dataset. Else, just keep the converted data. Default
|
|
132
130
|
is False.
|
|
133
|
-
**kwargs : Arguments to pass to Otter.query(). Can be
|
|
134
|
-
|
|
135
|
-
|
|
131
|
+
**kwargs : Arguments to pass to Otter.query(). Can be::
|
|
132
|
+
|
|
133
|
+
names (list[str]): A list of names to get the metadata for
|
|
134
|
+
coords (SkyCoord): An astropy SkyCoord object with coordinates
|
|
136
135
|
to match to
|
|
137
|
-
radius
|
|
136
|
+
radius (float): The radius in arcseconds for a cone search,
|
|
138
137
|
default is 0.05"
|
|
139
|
-
minZ
|
|
140
|
-
maxZ
|
|
141
|
-
refs
|
|
138
|
+
minZ (float): The minimum redshift to search for
|
|
139
|
+
maxZ (float): The maximum redshift to search for
|
|
140
|
+
refs (list[str]): A list of ads bibcodes to match to. Will only
|
|
142
141
|
return metadata for transients that have this
|
|
143
142
|
as a reference.
|
|
144
|
-
hasSpec
|
|
143
|
+
hasSpec (bool): if True, only return events that have spectra.
|
|
145
144
|
|
|
146
145
|
Return:
|
|
147
|
-
|
|
148
|
-
|
|
146
|
+
The photometry for the requested transients that match the arguments.
|
|
147
|
+
Will be an astropy Table sorted by transient default name.
|
|
148
|
+
|
|
149
|
+
Raises:
|
|
150
|
+
FailedQueryError: When the query returns no results
|
|
151
|
+
IOError: if one of your inputs is incorrect
|
|
149
152
|
"""
|
|
150
153
|
queryres = self.query(hasphot=True, **kwargs)
|
|
151
154
|
|
|
@@ -153,16 +156,25 @@ class Otter(object):
|
|
|
153
156
|
for transient in queryres:
|
|
154
157
|
# clean the photometry
|
|
155
158
|
default_name = transient["name/default_name"]
|
|
156
|
-
phot = transient.clean_photometry(
|
|
157
|
-
flux_unit=flux_unit,
|
|
158
|
-
date_unit=date_unit,
|
|
159
|
-
wave_unit=wave_unit,
|
|
160
|
-
freq_unit=freq_unit,
|
|
161
|
-
obs_type=obs_type,
|
|
162
|
-
)
|
|
163
|
-
phot["name"] = [default_name] * len(phot)
|
|
164
159
|
|
|
165
|
-
|
|
160
|
+
try:
|
|
161
|
+
phot = transient.clean_photometry(
|
|
162
|
+
flux_unit=flux_unit,
|
|
163
|
+
date_unit=date_unit,
|
|
164
|
+
wave_unit=wave_unit,
|
|
165
|
+
freq_unit=freq_unit,
|
|
166
|
+
obs_type=obs_type,
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
phot["name"] = [default_name] * len(phot)
|
|
170
|
+
|
|
171
|
+
dicts.append(phot)
|
|
172
|
+
|
|
173
|
+
except FailedQueryError:
|
|
174
|
+
# This is fine, it just means that there is no data associated
|
|
175
|
+
# with this one transient. We'll check and make sure there is data
|
|
176
|
+
# associated with at least one of the transients later!
|
|
177
|
+
pass
|
|
166
178
|
|
|
167
179
|
if len(dicts) == 0:
|
|
168
180
|
raise FailedQueryError()
|
|
@@ -180,11 +192,16 @@ class Otter(object):
|
|
|
180
192
|
"converted_date_unit",
|
|
181
193
|
"converted_wave_unit",
|
|
182
194
|
"converted_freq_unit",
|
|
195
|
+
"filter_key",
|
|
183
196
|
"obs_type",
|
|
184
197
|
"upperlimit",
|
|
185
198
|
"reference",
|
|
199
|
+
"human_readable_refs",
|
|
186
200
|
]
|
|
187
201
|
|
|
202
|
+
if "upperlimit" not in fullphot:
|
|
203
|
+
fullphot["upperlimit"] = False
|
|
204
|
+
|
|
188
205
|
if not keep_raw:
|
|
189
206
|
if "telescope" in fullphot:
|
|
190
207
|
fullphot = fullphot[keys_to_keep + ["telescope"]]
|
|
@@ -203,7 +220,7 @@ class Otter(object):
|
|
|
203
220
|
Loads an otter JSON file
|
|
204
221
|
|
|
205
222
|
Args:
|
|
206
|
-
filename
|
|
223
|
+
filename (str): The path to the OTTER JSON file to load
|
|
207
224
|
"""
|
|
208
225
|
|
|
209
226
|
# read in files from summary
|
|
@@ -234,15 +251,15 @@ class Otter(object):
|
|
|
234
251
|
same units.
|
|
235
252
|
|
|
236
253
|
Args:
|
|
237
|
-
names
|
|
238
|
-
coords
|
|
239
|
-
radius
|
|
240
|
-
minz
|
|
241
|
-
maxz
|
|
242
|
-
refs
|
|
254
|
+
names (list[str]): A list of names to get the metadata for
|
|
255
|
+
coords (SkyCoord): An astropy SkyCoord object with coordinates to match to
|
|
256
|
+
radius (float): The radius in arcseconds for a cone search, default is 0.05"
|
|
257
|
+
minz (float): The minimum redshift to search for
|
|
258
|
+
maxz (float): The maximum redshift to search for
|
|
259
|
+
refs (list[str]): A list of ads bibcodes to match to. Will only return
|
|
243
260
|
metadata for transients that have this as a reference.
|
|
244
|
-
hasphot
|
|
245
|
-
hasspec
|
|
261
|
+
hasphot (bool): if True, only returns transients which have photometry.
|
|
262
|
+
hasspec (bool): if True, only return transients that have spectra.
|
|
246
263
|
|
|
247
264
|
Return:
|
|
248
265
|
Get all of the raw (unconverted!) data for objects that match the criteria.
|
|
@@ -273,7 +290,6 @@ class Otter(object):
|
|
|
273
290
|
|
|
274
291
|
# then read and query the summary table
|
|
275
292
|
summary = pd.read_csv(summary_table)
|
|
276
|
-
|
|
277
293
|
# coordinate search first
|
|
278
294
|
if coords is not None:
|
|
279
295
|
if not isinstance(coords, SkyCoord):
|
|
@@ -344,24 +360,29 @@ class Otter(object):
|
|
|
344
360
|
|
|
345
361
|
return outdata
|
|
346
362
|
|
|
347
|
-
def save(self, schema: list[dict], testing=False
|
|
363
|
+
def save(self, schema: list[dict], testing=False) -> None:
|
|
348
364
|
"""
|
|
349
365
|
Upload all the data in the given list of schemas.
|
|
350
366
|
|
|
351
367
|
Args:
|
|
352
|
-
schema
|
|
368
|
+
schema (list[dict]): A list of json dictionaries
|
|
369
|
+
testing (bool): Should we just enter test mode? Default is False
|
|
370
|
+
|
|
371
|
+
Raises:
|
|
372
|
+
OtterLimitationError: If some objects in OTTER are within 5" we can't figure
|
|
373
|
+
out which ones to merge with which ones.
|
|
353
374
|
"""
|
|
354
375
|
|
|
355
376
|
if not isinstance(schema, list):
|
|
356
377
|
schema = [schema]
|
|
357
378
|
|
|
358
379
|
for transient in schema:
|
|
359
|
-
print(transient["name/default_name"])
|
|
360
|
-
|
|
361
380
|
# convert the json to a Transient
|
|
362
381
|
if not isinstance(transient, Transient):
|
|
363
382
|
transient = Transient(transient)
|
|
364
383
|
|
|
384
|
+
print(transient["name/default_name"])
|
|
385
|
+
|
|
365
386
|
coord = transient.get_skycoord()
|
|
366
387
|
res = self.cone_search(coords=coord)
|
|
367
388
|
|
|
@@ -405,8 +426,6 @@ class Otter(object):
|
|
|
405
426
|
outfilepath = os.path.join(self.DATADIR, todel[0] + ".json")
|
|
406
427
|
if test_mode:
|
|
407
428
|
print("Renaming the following file for backups: ", outfilepath)
|
|
408
|
-
else:
|
|
409
|
-
os.rename(outfilepath, outfilepath + ".backup")
|
|
410
429
|
else:
|
|
411
430
|
if test_mode:
|
|
412
431
|
print("Don't need to mess with the files at all!")
|
|
@@ -429,13 +448,16 @@ class Otter(object):
|
|
|
429
448
|
print(f"Would write to {outfilepath}")
|
|
430
449
|
# print(out)
|
|
431
450
|
|
|
432
|
-
def generate_summary_table(self, save=False):
|
|
451
|
+
def generate_summary_table(self, save=False) -> pd.DataFrame:
|
|
433
452
|
"""
|
|
434
453
|
Generate a summary table for the JSON files in self.DATADIR
|
|
435
454
|
|
|
436
455
|
args:
|
|
437
|
-
save
|
|
438
|
-
in self.DATADIR. Default is False.
|
|
456
|
+
save (bool): if True, save the summary file to "summary.csv"
|
|
457
|
+
in self.DATADIR. Default is False and is just returned.
|
|
458
|
+
|
|
459
|
+
returns:
|
|
460
|
+
pandas.DataFrame of the summary meta information of the transients
|
|
439
461
|
"""
|
|
440
462
|
allfiles = glob.glob(os.path.join(self.DATADIR, "*.json"))
|
|
441
463
|
|
|
@@ -455,10 +477,14 @@ class Otter(object):
|
|
|
455
477
|
}
|
|
456
478
|
|
|
457
479
|
if "date_reference" in t:
|
|
458
|
-
|
|
480
|
+
date_types = {d["date_type"] for d in t["date_reference"]}
|
|
481
|
+
if "discovery" in date_types:
|
|
482
|
+
row["discovery_date"] = t.get_discovery_date()
|
|
459
483
|
|
|
460
484
|
if "distance" in t:
|
|
461
|
-
|
|
485
|
+
dist_types = {d["distance_type"] for d in t["distance"]}
|
|
486
|
+
if "redshift" in dist_types:
|
|
487
|
+
row["z"] = t.get_redshift()
|
|
462
488
|
|
|
463
489
|
row["hasPhot"] = "photometry" in t
|
|
464
490
|
row["hasSpec"] = "spectra" in t
|