1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 from __future__ import division
19
20 __author__ = "Nickolas Fotopoulos <nvf@gravity.phys.uwm.edu>"
21
22 import itertools
23 import math
24
25 import numpy
26 import warnings
27
28
29
30
31
33 """
34 Make an array of angles completely between -pi and pi.
35 """
36 return angles - numpy.sign(angles) * \
37 numpy.round(abs(angles) / (2 * numpy.pi)) * 2 * numpy.pi
38
40 """
41 Return True if the (2-D) point is inside the (2-D) polygon defined by the
42 vertices.
43
44 Warning: Result is undefined for points lying along the edge of the polygon.
45
46 Adapted from:
47 http://local.wasp.uwa.edu.au/~pbourke/geometry/insidepoly/ (solution 2)
48 """
49 point = numpy.array(point)
50 centered_vertices = numpy.empty(shape=(len(vertices) + 1, 2), dtype=float)
51 centered_vertices[:-1] = vertices - point
52 centered_vertices[-1] = centered_vertices[0]
53 angles = numpy.arctan2(centered_vertices[:, 1], centered_vertices[:, 0])
54 diff_angles = _make_within_pi(angles[1:] - angles[:-1])
55 angle_sum = diff_angles.sum()
56 return abs(angle_sum) >= numpy.pi
57
59 """
60 Return True if dmax > gal_dist > dmin
61 """
62 if (dmin > dmax):
63 raise ValueError, "minimum distance is greater than maximum distance "\
64 + str(dmin) + " > "+ str(dmax)
65 if (0 > dmin) or (0 > dmax):
66 raise ValueError, "negative distance " + str(dmin) + " " + str(dmax)
67 return (dmax > gal_dist) and (dmin < gal_dist)
68
69
70
71
72
73
75 """
76 Convert right ascension and from a sexagesimal string to floating point
77 radians. Handles h:m:s or h:m.
78 """
79 tup = ra_sex.split(":")
80 h = int(tup[0])
81 m = int(tup[1])
82 s = float(tup[2])
83
84 if (h < 0 or h > 23) or (m < 0 or m > 60) or (s < 0 or s >= 60):
85 raise ValueError, "hour, minute, or second out of bounds " + ra_sex
86 return 2 * numpy.pi * (h + (m + s / 60) / 60) / 24
87
89 """
90 Convert declination from a colon-delimited sexagesimal string to floating
91 point radians. Handles d:m:s.
92 """
93 tup = dec_sex.split(":")
94 if tup[0].startswith("-"):
95 d = int(tup[0][1:])
96 sign = -1
97 else:
98 d = int(tup[0])
99 sign = +1
100 m = int(tup[1])
101 s = float(tup[2])
102
103 if (d > 89) or (m < 0 or m > 60) or (s < 0 or s >= 60):
104 raise ValueError, "degree, minute, or second out of bounds: " + dec_sex
105 return numpy.pi * sign * (d + (m + s / 60) / 60) / 180
106
108 """
109 Convert right ascension and from a sexagesimal string to floating point
110 radians. Handles h:m.
111 """
112 tup = ra_sex.split(":")
113 h = int(tup[0])
114 m = float(tup[1])
115
116 if (h < 0 or h > 23) or (m < 0 or m > 60):
117 raise ValueError, "hour or minute out of bounds " + ra_sex
118 return 2 * numpy.pi * (h + m / 60) / 24
119
121 """
122 Convert declination from a colon-delimited sexagesimal string to floating
123 point radians. Handles d:m.
124 """
125 tup = dec_sex.split(":")
126 if tup[0].startswith("-"):
127 d = int(tup[0][1:])
128 sign = -1
129 else:
130 d = int(tup[0])
131 sign = 1
132 m = float(tup[1])
133
134
135 if (d > 89) or (m < 0 or m > 60):
136 raise ValueError, "degree or minute out of bounds: " + dec_sex
137 return numpy.pi * sign * (d + m / 60) / 180
138
140 """
141 convert arcminutes to radians
142 """
143 if amins == '~':
144 return numpy.nan
145 else:
146 return 10800*float(amins)/numpy.pi
147
149 """
150 convert degrees to radians
151 """
152 if degs == '~':
153 return numpy.nan
154 else:
155 return float(degs)*numpy.pi/180
156
158 """
159 convert hours to radians
160 """
161 return float(hours)*numpy.pi/12
162
164 """
165 deal with the use of tildes (and other strings) for unknown float quantities
166 in the GWGC catalog
167 """
168 if num == '~':
169 return numpy.nan
170 else:
171 return float(num)
172
174 """
175 deal with the use of tildes for unknown int quantities
176 in the GWGC catalog
177 """
178 if num == '~':
179 return None
180 else:
181 return int(num)
182
183
184
185
186
187
189 """
190 class for working with the galaxy catalog created and maintained by the
191 CBC group
192
193 Current catalog:
194 http://www.lsc-group.phys.uwm.edu/cgit/lalsuite/plain/lalapps/src/inspiral/inspsrcs100Mpc.errors
195
196 Literature reference:
197 http://arxiv.org/pdf/0706.1283
198 """
199 valid_columns = {
200 "name": (0, str),
201 "ra": (1, hm2rad),
202 "dec": (2, dm2rad),
203
204 "distance_kpc": (3, float),
205
206 "luminosity_mwe": (4, float),
207 "metal_correction": (5, float),
208 "magnitude_error": (6, float),
209 "distance_error": (7, float),
210 }
211
212 - def entry_from_line(cls, line, load_columns):
213
214 row = cls.entry_class()
215
216
217 tup = line.split()
218
219
220 for col_name in load_columns:
221 col_index, col_type = cls.valid_columns[col_name]
222 setattr(row, col_name, col_type(tup[col_index]))
223
224 return row
225 entry_from_line = classmethod(entry_from_line)
226
227 - def from_file(cls, fileobj, load_columns=None):
228
229 if load_columns is None:
230 load_columns = cls.valid_columns.keys()
231 else:
232 for col in load_columns:
233 if col not in cls.valid_columns:
234 raise ValueError, "no such column exists"
235 cls.entry_class.__slots__ = load_columns
236
237
238 return cls([cls.entry_from_line(line, load_columns) for line \
239 in fileobj if not line.startswith("#")])
240 from_file = classmethod(from_file)
241
245
247 return self.__class__([gal for gal in self if \
248 is_within_distances(gal.distance_kpc, dmin, dmax)])
249
251 return "\n".join(itertools.imap(str, self))
252
254 """
255 left here to maintain compatibility with existing codes
256 """
258 warnings.warn( \
259 'The GalaxyCatalog class has been replaced by the CBCGC class.',
260 DeprecationWarning, stacklevel=3)
261 CBCGC.__init__(self,*args,**kwargs)
262
264 """
265 useful class for dealing with the gravitational wave galaxy catalog
266
267 Current catalog:
268 https://www.lsc-group.phys.uwm.edu/cgi-bin/pcvs/viewcvs.cgi/bursts/collabs/DO_proposal/gwgc/GWGCCatalog.txt?rev=1.4&content-type=text/vnd.viewcvs-markup
269 """
270 valid_columns = {
271
272 "pgc": (0,int_or_tilde),
273 "name": (1, str),
274 "ra": (2, h2rad),
275 "dec": (3, deg2rad_or_tilde),
276
277 "mtype": (4, str),
278
279 "app_mag": (5, float_or_tilde),
280
281 "maj_diam": (6, amin2rad_or_tilde),
282
283 "maj_diam_error": (7, amin2rad_or_tilde),
284
285 "min_diam": (8, amin2rad_or_tilde),
286
287 "min_diam_error": (9, amin2rad_or_tilde),
288
289 "ratio_diams": (10, float_or_tilde),
290
291 "ratio_diams_error": (11, float_or_tilde),
292
293 "pos_ang": (12, deg2rad_or_tilde),
294
295 "abs_mag": (13, float_or_tilde),
296
297 "distance_mpc": (14, float_or_tilde),
298
299 "distance_error": (15, float_or_tilde),
300
301 "app_mag_error": (16, float_or_tilde),
302
303 "abs_mag_error": (17, float_or_tilde)
304 }
305
306 - def entry_from_line(cls, line, load_columns):
307
308 row = cls.entry_class()
309
310
311 tup = line.split('|')
312
313
314 for col_name in load_columns:
315 col_index, col_type = cls.valid_columns[col_name]
316 setattr(row, col_name, col_type(tup[col_index]))
317 return row
318 entry_from_line = classmethod(entry_from_line)
319
320 - def from_file(cls, fileobj, load_columns=None):
321
322 if load_columns is None:
323 load_columns = cls.valid_columns.keys()
324 else:
325 for col in load_columns:
326 if col not in cls.valid_columns:
327 raise ValueError, "no such column exists"
328 cls.entry_class.__slots__ = load_columns
329
330
331 return cls([cls.entry_from_line(line, load_columns) for line \
332 in fileobj if not line.startswith("#")])
333 from_file = classmethod(from_file)
334
336 return self.__class__([gal for gal in self if \
337 is_within_distances(gal.distance_mpc, dmin, dmax)])
338
340 """
341 A galaxy object that knows how to initialize itself from a line in a text
342 file and consumes a minimum of memory.
343 """
344 __slots__ = GalaxyCatalog.valid_columns.keys()
345
347 return "\t".join([str(getattr(self, slot)) for slot in self.__slots__])
348
350 return "Galaxy(\"" + str(self) + "\")"
351
353 return (self.ra, self.dec)
354 coords = property(fget=_coords_getter)
355
358
359 GalaxyCatalog.entry_class = CBCGGalaxy
360 CBCGC.entry_class = CBCGGalaxy
361 GWGC.entry_class = GWGCgalaxy
362