1 | # Licensed under a 3-clause BSD style license - see LICENSE.rst |
||
2 | """Simulate source catalogs.""" |
||
3 | from __future__ import absolute_import, division, print_function, unicode_literals |
||
4 | import numpy as np |
||
5 | from ...extern import six |
||
6 | from astropy.table import Table, Column |
||
0 ignored issues
–
show
introduced
by
Loading history...
|
|||
7 | from astropy.units import Quantity |
||
0 ignored issues
–
show
|
|||
8 | from astropy.coordinates import SkyCoord, spherical_to_cartesian |
||
0 ignored issues
–
show
|
|||
9 | from ...utils import coordinates as astrometry |
||
10 | from ...utils.coordinates import D_SUN_TO_GALACTIC_CENTER |
||
11 | from ...utils.distributions import draw, pdf |
||
12 | from ...utils.random import sample_sphere, sample_sphere_distance, get_random_state |
||
13 | from ..source import SNR, SNRTrueloveMcKee, PWN, Pulsar |
||
14 | from ..population.spatial import ( |
||
15 | Exponential, |
||
16 | FaucherSpiral, |
||
17 | RMIN, |
||
18 | RMAX, |
||
19 | ZMIN, |
||
20 | ZMAX, |
||
21 | radial_distributions, |
||
22 | ) |
||
23 | from ..population.velocity import VMIN, VMAX, velocity_distributions |
||
24 | |||
25 | __all__ = [ |
||
26 | "make_catalog_random_positions_cube", |
||
27 | "make_catalog_random_positions_sphere", |
||
28 | "make_base_catalog_galactic", |
||
29 | "add_snr_parameters", |
||
30 | "add_pulsar_parameters", |
||
31 | "add_pwn_parameters", |
||
32 | "add_observed_source_parameters", |
||
33 | "add_observed_parameters", |
||
34 | ] |
||
35 | |||
36 | |||
37 | def make_catalog_random_positions_cube( |
||
38 | size=100, dimension=3, dmax=10, random_state="random-seed" |
||
0 ignored issues
–
show
|
|||
39 | ): |
||
40 | """Make a catalog of sources randomly distributed on a line, square or cube. |
||
41 | |||
42 | TODO: is this useful enough for general use or should we hide it as an |
||
43 | internal method to generate test datasets? |
||
44 | |||
45 | Parameters |
||
46 | ---------- |
||
47 | size : int, optional |
||
48 | Number of sources |
||
49 | dimension : int, optional |
||
50 | Number of dimensions |
||
51 | dmax : int, optional |
||
52 | Maximum distance in pc. |
||
53 | random_state : {int, 'random-seed', 'global-rng', `~numpy.random.RandomState`} |
||
54 | Defines random number generator initialisation. |
||
55 | Passed to `~gammapy.utils.random.get_random_state`. |
||
56 | |||
57 | Returns |
||
58 | ------- |
||
59 | catalog : `~astropy.table.Table` |
||
60 | Source catalog with columns: |
||
61 | """ |
||
62 | random_state = get_random_state(random_state) |
||
63 | |||
64 | # Generate positions 1D, 2D, or 3D |
||
65 | if dimension == 3: |
||
66 | x = random_state.uniform(-dmax, dmax, size) |
||
67 | y = random_state.uniform(-dmax, dmax, size) |
||
68 | z = random_state.uniform(-dmax, dmax, size) |
||
69 | elif dimension == 2: |
||
70 | x = random_state.uniform(-dmax, dmax, size) |
||
71 | y = random_state.uniform(-dmax, dmax, size) |
||
72 | z = np.zeros_like(x) |
||
73 | else: |
||
74 | x = random_state.uniform(-dmax, dmax, size) |
||
75 | y = np.zeros_like(x) |
||
76 | z = np.zeros_like(x) |
||
77 | |||
78 | table = Table() |
||
79 | table["x"] = Column(x, unit="pc", description="Galactic cartesian coordinate") |
||
80 | table["y"] = Column(y, unit="pc", description="Galactic cartesian coordinate") |
||
81 | table["z"] = Column(z, unit="pc", description="Galactic cartesian coordinate") |
||
82 | |||
83 | return table |
||
84 | |||
85 | |||
86 | def make_catalog_random_positions_sphere( |
||
87 | size, center="Earth", distance=Quantity([0, 1], "Mpc"), random_state="random-seed" |
||
0 ignored issues
–
show
|
|||
88 | ): |
||
89 | """Sample random source locations in a sphere. |
||
90 | |||
91 | This can be used to generate an isotropic source population |
||
92 | to represent extra-galactic sources. |
||
93 | |||
94 | Parameters |
||
95 | ---------- |
||
96 | size : int |
||
97 | Number of sources |
||
98 | center : {'Earth', 'Milky Way'} |
||
99 | Sphere center |
||
100 | distance : `~astropy.units.Quantity` tuple |
||
101 | Distance min / max range. |
||
102 | random_state : {int, 'random-seed', 'global-rng', `~numpy.random.RandomState`} |
||
103 | Defines random number generator initialisation. |
||
104 | Passed to `~gammapy.utils.random.get_random_state`. |
||
105 | |||
106 | Returns |
||
107 | ------- |
||
108 | catalog : `~astropy.table.Table` |
||
109 | Source catalog with columns: |
||
110 | |||
111 | - RAJ2000, DEJ2000 (deg) |
||
112 | - GLON, GLAT (deg) |
||
113 | - Distance (Mpc) |
||
114 | """ |
||
115 | random_state = get_random_state(random_state) |
||
116 | |||
117 | lon, lat = sample_sphere(size, random_state=random_state) |
||
118 | radius = sample_sphere_distance( |
||
119 | distance[0], distance[1], size, random_state=random_state |
||
120 | ) |
||
121 | |||
122 | # TODO: it shouldn't be necessary here to convert to cartesian ourselves ... |
||
0 ignored issues
–
show
|
|||
123 | x, y, z = spherical_to_cartesian(radius, lat, lon) |
||
124 | pos = SkyCoord(x, y, z, frame="galactocentric", representation="cartesian") |
||
125 | |||
126 | if center == "Milky Way": |
||
127 | pass |
||
128 | elif center == "Earth": |
||
129 | # TODO: add shift Galactic center -> Earth |
||
0 ignored issues
–
show
|
|||
130 | raise NotImplementedError |
||
131 | else: |
||
132 | msg = "Invalid center: {}\n".format(center) |
||
133 | msg += "Choose one of: Earth, Milky Way" |
||
134 | raise ValueError(msg) |
||
135 | |||
136 | table = Table() |
||
137 | table.meta["center"] = center |
||
138 | |||
139 | icrs = pos.transform_to("icrs") |
||
140 | table["RAJ2000"] = icrs.ra.to("deg") |
||
141 | table["DEJ2000"] = icrs.dec.to("deg") |
||
142 | |||
143 | galactic = icrs.transform_to("galactic") |
||
144 | table["GLON"] = galactic.l.to("deg") |
||
145 | table["GLAT"] = galactic.b.to("deg") |
||
146 | |||
147 | table["Distance"] = icrs.distance.to("Mpc") |
||
148 | |||
149 | return table |
||
150 | |||
151 | |||
152 | def make_base_catalog_galactic( |
||
0 ignored issues
–
show
|
|||
153 | n_sources, |
||
0 ignored issues
–
show
|
|||
154 | rad_dis="YK04", |
||
0 ignored issues
–
show
|
|||
155 | vel_dis="H05", |
||
0 ignored issues
–
show
|
|||
156 | max_age=Quantity(1e6, "yr"), |
||
0 ignored issues
–
show
|
|||
157 | spiralarms=True, |
||
0 ignored issues
–
show
|
|||
158 | n_ISM=Quantity(1, "cm-3"), |
||
0 ignored issues
–
show
|
|||
159 | random_state="random-seed", |
||
0 ignored issues
–
show
|
|||
160 | ): |
||
161 | """Make a catalog of Galactic sources, with basic source parameters. |
||
162 | |||
163 | Choose a radial distribution, a velocity distribution, the number |
||
164 | of pulsars n_pulsars, the maximal age max_age[years] and the fraction |
||
165 | of the individual morphtypes. There's an option spiralarms. If set on |
||
166 | True a spiralarm modelling after Faucher&Kaspi is included. |
||
167 | |||
168 | max_age and n_sources effectively correspond to s SN rate: |
||
169 | SN_rate = n_sources / max_age |
||
170 | |||
171 | Parameters |
||
172 | ---------- |
||
173 | n_sources : int |
||
174 | Number of sources to simulate. |
||
175 | rad_dis : callable |
||
176 | Radial surface density distribution of sources. |
||
177 | vel_dis : callable |
||
178 | Proper motion velocity distribution of sources. |
||
179 | max_age : `~astropy.units.Quantity` |
||
180 | Maximal age of the source |
||
181 | spiralarms : bool |
||
182 | Include a spiralarm model in the catalog. |
||
183 | n_ISM : `~astropy.units.Quantity` |
||
184 | Density of the interstellar medium. |
||
185 | random_state : {int, 'random-seed', 'global-rng', `~numpy.random.RandomState`} |
||
186 | Defines random number generator initialisation. |
||
187 | Passed to `~gammapy.utils.random.get_random_state`. |
||
188 | |||
189 | Returns |
||
190 | ------- |
||
191 | table : `~astropy.table.Table` |
||
192 | Catalog of simulated source positions and proper velocities. |
||
193 | """ |
||
194 | random_state = get_random_state(random_state) |
||
195 | |||
196 | if isinstance(rad_dis, six.string_types): |
||
197 | rad_dis = radial_distributions[rad_dis] |
||
198 | |||
199 | if isinstance(vel_dis, six.string_types): |
||
200 | vel_dis = velocity_distributions[vel_dis] |
||
201 | |||
202 | # Draw random values for the age |
||
203 | age = random_state.uniform(0, max_age.to_value("yr"), n_sources) |
||
204 | age = Quantity(age, "yr") |
||
205 | |||
206 | # Draw r and z values from the given distribution |
||
207 | r = draw( |
||
208 | RMIN.to_value("kpc"), |
||
209 | RMAX.to_value("kpc"), |
||
210 | n_sources, |
||
211 | pdf(rad_dis()), |
||
212 | random_state=random_state, |
||
213 | ) |
||
214 | r = Quantity(r, "kpc") |
||
215 | |||
216 | z = draw( |
||
217 | ZMIN.to_value("kpc"), |
||
218 | ZMAX.to_value("kpc"), |
||
219 | n_sources, |
||
220 | Exponential(), |
||
221 | random_state=random_state, |
||
222 | ) |
||
223 | z = Quantity(z, "kpc") |
||
224 | |||
225 | # Apply spiralarm modelling or not |
||
226 | if spiralarms: |
||
227 | r, theta, spiralarm = FaucherSpiral()(r, random_state=random_state) |
||
228 | else: |
||
229 | theta = Quantity(random_state.uniform(0, 2 * np.pi, n_sources), "rad") |
||
230 | spiralarm = None |
||
231 | |||
232 | # Compute cartesian coordinates |
||
233 | x, y = astrometry.cartesian(r, theta) |
||
234 | |||
235 | # Draw values from velocity distribution |
||
236 | v = draw( |
||
237 | VMIN.to_value("km/s"), |
||
238 | VMAX.to_value("km/s"), |
||
239 | n_sources, |
||
240 | vel_dis(), |
||
241 | random_state=random_state, |
||
242 | ) |
||
243 | v = Quantity(v, "km/s") |
||
244 | |||
245 | # Draw random direction of initial velocity |
||
246 | theta = Quantity(random_state.uniform(0, np.pi, x.size), "rad") |
||
247 | phi = Quantity(random_state.uniform(0, 2 * np.pi, x.size), "rad") |
||
248 | |||
249 | # Compute new position |
||
250 | dx, dy, dz, vx, vy, vz = astrometry.motion_since_birth(v, age, theta, phi) |
||
251 | |||
252 | # Add displacement to birth position |
||
253 | x_moved = x + dx |
||
254 | y_moved = y + dy |
||
255 | z_moved = z + dz |
||
256 | |||
257 | # Set environment interstellar density |
||
258 | n_ISM = n_ISM * np.ones(n_sources) |
||
259 | |||
260 | table = Table() |
||
261 | table["age"] = Column(age, unit="yr", description="Age of the source") |
||
262 | table["n_ISM"] = Column( |
||
263 | n_ISM, unit="cm-3", description="Interstellar medium density" |
||
264 | ) |
||
265 | if spiralarms: |
||
266 | table["spiralarm"] = Column(spiralarm, description="Which spiralarm?") |
||
267 | |||
268 | table["x_birth"] = Column( |
||
269 | x, unit="kpc", description="Galactocentric x coordinate at birth" |
||
270 | ) |
||
271 | table["y_birth"] = Column( |
||
272 | y, unit="kpc", description="Galactocentric y coordinate at birth" |
||
273 | ) |
||
274 | table["z_birth"] = Column( |
||
275 | z, unit="kpc", description="Galactocentric z coordinate at birth" |
||
276 | ) |
||
277 | |||
278 | table["x"] = Column( |
||
279 | x_moved.to("kpc"), unit="kpc", description="Galactocentric x coordinate" |
||
280 | ) |
||
281 | table["y"] = Column( |
||
282 | y_moved.to("kpc"), unit="kpc", description="Galactocentric y coordinate" |
||
283 | ) |
||
284 | table["z"] = Column( |
||
285 | z_moved.to("kpc"), unit="kpc", description="Galactocentric z coordinate" |
||
286 | ) |
||
287 | |||
288 | table["vx"] = Column( |
||
289 | vx.to("km/s"), unit="km/s", description="Galactocentric velocity in x direction" |
||
290 | ) |
||
291 | table["vy"] = Column( |
||
292 | vy.to("km/s"), unit="km/s", description="Galactocentric velocity in y direction" |
||
293 | ) |
||
294 | table["vz"] = Column( |
||
295 | vz.to("km/s"), unit="km/s", description="Galactocentric velocity in z direction" |
||
296 | ) |
||
297 | table["v_abs"] = Column( |
||
298 | v, unit="km/s", description="Galactocentric velocity (absolute)" |
||
299 | ) |
||
300 | |||
301 | return table |
||
302 | |||
303 | |||
304 | def add_snr_parameters(table): |
||
305 | """Add SNR parameters to the table.""" |
||
306 | # Read relevant columns |
||
307 | age = table["age"].quantity |
||
308 | n_ISM = table["n_ISM"].quantity |
||
309 | |||
310 | # Compute properties |
||
311 | snr = SNR(n_ISM=n_ISM) |
||
312 | E_SN = snr.e_sn * np.ones(len(table)) |
||
313 | r_out = snr.radius(age) |
||
314 | r_in = snr.radius_inner(age) |
||
315 | L_SNR = snr.luminosity_tev(age) |
||
316 | |||
317 | # Add columns to table |
||
318 | table["E_SN"] = Column(E_SN, unit="erg", description="SNR kinetic energy") |
||
319 | table["r_out"] = Column(r_out, unit="pc", description="SNR outer radius") |
||
320 | table["r_in"] = Column(r_in, unit="pc", description="SNR inner radius") |
||
321 | table["L_SNR"] = Column(L_SNR, unit="s-1", description="SNR luminosity") |
||
322 | return table |
||
323 | |||
324 | |||
325 | def add_pulsar_parameters( |
||
0 ignored issues
–
show
|
|||
326 | table, |
||
0 ignored issues
–
show
|
|||
327 | B_mean=12.05, |
||
0 ignored issues
–
show
|
|||
328 | B_stdv=0.55, |
||
0 ignored issues
–
show
|
|||
329 | P_mean=0.3, |
||
0 ignored issues
–
show
|
|||
330 | P_stdv=0.15, |
||
0 ignored issues
–
show
|
|||
331 | random_state="random-seed", |
||
0 ignored issues
–
show
|
|||
332 | ): |
||
333 | """Add pulsar parameters to the table. |
||
334 | |||
335 | For the initial normal distribution of period and logB can exist the following |
||
336 | Parameters: B_mean=12.05[log Gauss], B_stdv=0.55, P_mean=0.3[s], P_stdv=0.15 |
||
337 | |||
338 | Parameters |
||
339 | ---------- |
||
340 | random_state : {int, 'random-seed', 'global-rng', `~numpy.random.RandomState`} |
||
341 | Defines random number generator initialisation. |
||
342 | Passed to `~gammapy.utils.random.get_random_state`. |
||
343 | |||
344 | """ |
||
345 | random_state = get_random_state(random_state) |
||
346 | # Read relevant columns |
||
347 | age = table["age"].quantity |
||
348 | |||
349 | # Draw the initial values for the period and magnetic field |
||
350 | def p_dist(x): |
||
351 | return np.exp(-0.5 * ((x - P_mean) / P_stdv) ** 2) |
||
352 | |||
353 | p0_birth = draw(0, 2, len(table), p_dist, random_state=random_state) |
||
354 | p0_birth = Quantity(p0_birth, "s") |
||
355 | |||
356 | logB = random_state.normal(B_mean, B_stdv, len(table)) |
||
357 | |||
358 | # Compute pulsar parameters |
||
359 | psr = Pulsar(p0_birth, logB) |
||
360 | p0 = psr.period(age) |
||
361 | p1 = psr.period_dot(age) |
||
362 | p1_birth = psr.P_dot_0 |
||
363 | tau = psr.tau(age) |
||
364 | tau_0 = psr.tau_0 |
||
365 | l_psr = psr.luminosity_spindown(age) |
||
366 | l0_psr = psr.L_0 |
||
367 | |||
368 | # Add columns to table |
||
369 | table["P0"] = Column(p0, unit="s", description="Pulsar period") |
||
370 | table["P1"] = Column(p1, unit="", description="Pulsar period derivative") |
||
371 | table["P0_birth"] = Column(p0_birth, unit="s", description="Pulsar birth period") |
||
372 | table["P1_birth"] = Column( |
||
373 | p1_birth, unit="", description="Pulsar birth period derivative" |
||
374 | ) |
||
375 | table["CharAge"] = Column(tau, unit="yr", description="Pulsar characteristic age") |
||
376 | table["Tau0"] = Column(tau_0, unit="yr") |
||
377 | table["L_PSR"] = Column(l_psr, unit="erg s-1") |
||
378 | table["L0_PSR"] = Column(l0_psr, unit="erg s-1") |
||
379 | table["logB"] = Column(logB, unit="Gauss") |
||
380 | return table |
||
381 | |||
382 | |||
383 | def add_pwn_parameters(table): |
||
384 | """Add PWN parameters to the table.""" |
||
385 | # Some of the computations (specifically `pwn.radius`) aren't vectorised |
||
386 | # across all parameters; so here we loop over source parameters explicitly |
||
387 | |||
388 | results = [] |
||
389 | |||
390 | for idx in range(len(table)): |
||
391 | age = table["age"].quantity[idx] |
||
392 | E_SN = table["E_SN"].quantity[idx] |
||
393 | n_ISM = table["n_ISM"].quantity[idx] |
||
394 | P0_birth = table["P0_birth"].quantity[idx] |
||
395 | logB = table["logB"][idx] |
||
396 | |||
397 | # Compute properties |
||
398 | pulsar = Pulsar(P0_birth, logB) |
||
399 | snr = SNRTrueloveMcKee(e_sn=E_SN, n_ISM=n_ISM) |
||
400 | pwn = PWN(pulsar, snr) |
||
401 | r_out_pwn = pwn.radius(age).to_value("pc") |
||
402 | L_PWN = pwn.luminosity_tev(age).to_value("erg") |
||
403 | results.append(dict(r_out_pwn=r_out_pwn, L_PWN=L_PWN)) |
||
404 | |||
405 | # Add columns to table |
||
406 | table["r_out_PWN"] = Column( |
||
407 | [_["r_out_pwn"] for _ in results], unit="pc", description="PWN outer radius" |
||
408 | ) |
||
409 | table["L_PWN"] = Column( |
||
410 | [_["L_PWN"] for _ in results], |
||
411 | unit="erg", |
||
412 | description="PWN luminosity above 1 TeV", |
||
413 | ) |
||
414 | return table |
||
415 | |||
416 | |||
417 | def add_observed_source_parameters(table): |
||
0 ignored issues
–
show
|
|||
418 | """Add observed source parameters to the table.""" |
||
419 | # Read relevant columns |
||
420 | distance = table["distance"] |
||
421 | r_in = table["r_in"] |
||
422 | r_out = table["r_out"] |
||
423 | r_out_PWN = table["r_out_PWN"] |
||
424 | L_SNR = table["L_SNR"] |
||
425 | L_PSR = table["L_PSR"] |
||
426 | L_PWN = table["L_PWN"] |
||
427 | |||
428 | # Compute properties |
||
429 | ext_in_SNR = astrometry.radius_to_angle(r_in, distance) |
||
430 | ext_out_SNR = astrometry.radius_to_angle(r_out, distance) |
||
431 | ext_out_PWN = astrometry.radius_to_angle(r_out_PWN, distance) |
||
432 | |||
433 | # Ellipse parameters not used for now |
||
434 | theta = np.pi / 2 * np.ones(len(table)) # Position angle? |
||
435 | epsilon = np.zeros(len(table)) # Ellipticity? |
||
436 | |||
437 | S_SNR = astrometry.luminosity_to_flux(L_SNR, distance) |
||
438 | # Ld2_PSR = astrometry.luminosity_to_flux(L_PSR, distance) |
||
439 | Ld2_PSR = L_PSR / distance ** 2 |
||
440 | S_PWN = astrometry.luminosity_to_flux(L_PWN, distance) |
||
441 | |||
442 | # Add columns |
||
443 | table["ext_in_SNR"] = Column(ext_in_SNR, unit="deg") |
||
444 | table["ext_out_SNR"] = Column(ext_out_SNR, unit="deg") |
||
445 | table["ext_out_PWN"] = Column(ext_out_PWN, unit="deg") |
||
446 | table["theta"] = Column(theta, unit="rad") |
||
447 | table["epsilon"] = Column(epsilon, unit="") |
||
448 | table["S_SNR"] = Column(S_SNR, unit="cm-2 s-1") |
||
449 | table["Ld2_PSR"] = Column(Ld2_PSR, unit="erg s-1 kpc-2") |
||
450 | table["S_PWN"] = Column(S_PWN, unit="cm-2 s-1") |
||
451 | return table |
||
452 | |||
453 | |||
454 | def add_observed_parameters(table, obs_pos=None): |
||
0 ignored issues
–
show
|
|||
455 | """Add observable parameters (such as sky position or distance). |
||
456 | |||
457 | Input table columns: x, y, z, extension, luminosity |
||
458 | |||
459 | Output table columns: distance, glon, glat, flux, angular_extension |
||
460 | |||
461 | Position of observer in cartesian coordinates. |
||
462 | Center of galaxy as origin, x-axis goes trough sun. |
||
463 | |||
464 | Parameters |
||
465 | ---------- |
||
466 | table : `~astropy.table.Table` |
||
467 | Input table |
||
468 | obs_pos : tuple or None |
||
469 | Observation position (X, Y, Z) in Galactocentric coordinates (default: Earth) |
||
470 | |||
471 | Returns |
||
472 | ------- |
||
473 | table : `~astropy.table.Table` |
||
474 | Modified input table with columns added |
||
475 | """ |
||
476 | obs_pos = obs_pos or [D_SUN_TO_GALACTIC_CENTER, 0, 0] |
||
477 | |||
478 | # Get data |
||
479 | x, y, z = table["x"].quantity, table["y"].quantity, table["z"].quantity |
||
480 | vx, vy, vz = table["vx"].quantity, table["vy"].quantity, table["vz"].quantity |
||
481 | |||
482 | distance, glon, glat = astrometry.galactic(x, y, z, obs_pos=obs_pos) |
||
483 | |||
484 | # Compute projected velocity |
||
485 | v_glon, v_glat = astrometry.velocity_glon_glat(x, y, z, vx, vy, vz) |
||
486 | |||
487 | coordinate = SkyCoord(glon, glat, unit="deg", frame="galactic").transform_to("icrs") |
||
488 | ra, dec = coordinate.ra.deg, coordinate.dec.deg |
||
489 | |||
490 | # Add columns to table |
||
491 | table["distance"] = Column( |
||
492 | distance, unit="pc", description="Distance observer to source center" |
||
493 | ) |
||
494 | table["GLON"] = Column(glon, unit="deg", description="Galactic longitude") |
||
495 | table["GLAT"] = Column(glat, unit="deg", description="Galactic latitude") |
||
496 | table["VGLON"] = Column( |
||
497 | v_glon.to("deg/Myr"), |
||
498 | unit="deg/Myr", |
||
499 | description="Velocity in Galactic longitude", |
||
500 | ) |
||
501 | table["VGLAT"] = Column( |
||
502 | v_glat.to("deg/Myr"), |
||
503 | unit="deg/Myr", |
||
504 | description="Velocity in Galactic latitude", |
||
505 | ) |
||
506 | table["RA"] = Column(ra, unit="deg", description="Right ascension") |
||
507 | table["DEC"] = Column(dec, unit="deg", description="Declination") |
||
508 | |||
509 | try: |
||
510 | luminosity = table["luminosity"] |
||
511 | flux = astrometry.luminosity_to_flux(luminosity, distance) |
||
512 | table["flux"] = Column(flux.value, unit=flux.unit, description="Source flux") |
||
513 | except KeyError: |
||
514 | pass |
||
515 | |||
516 | try: |
||
517 | extension = table["extension"] |
||
518 | angular_extension = np.degrees(np.arctan(extension / distance)) |
||
519 | table["angular_extension"] = Column( |
||
520 | angular_extension, |
||
521 | unit="deg", |
||
522 | description="Source angular radius (i.e. half-diameter)", |
||
523 | ) |
||
524 | except KeyError: |
||
525 | pass |
||
526 | |||
527 | return table |
||
528 |