def test_dist_to_dm(): """ Test that astropy units / angles work with dist_to_dm """ a = pygedm.dist_to_dm(204, -6.5, 200, method='ne2001') b = pygedm.dist_to_dm(Angle(204, unit='degree'), Angle(-6.5, unit='degree'), 200, method='ne2001') c = pygedm.dist_to_dm(204, -6.5, 200 * Unit('pc'), method='ne2001') assert a[0] == b[0] == c[0] assert a[1] == b[1] == c[1]
def test_tau_sc_nu(): """ Test that tau_sc changes with nu """ dm, tau_sc = pygedm.dist_to_dm(0, 0, 100, method='ymw16', nu=1) dm_, tau_sc_ = pygedm.dist_to_dm(0, 0, 100, method='ymw16', nu=1000*u.MHz) assert dm == dm_ assert tau_sc == tau_sc_ dm, tau_sc = pygedm.dist_to_dm(0, 0, 100, method='ne2001', nu=1) dm_, tau_sc_ = pygedm.dist_to_dm(0, 0, 100, method='ne2001', nu=1000*u.MHz) assert dm == dm_ assert tau_sc == tau_sc_ dist, tau_sc = pygedm.dist_to_dm(0, 0, 100, method='ymw16', nu=1) dist_, tau_sc_ = pygedm.dist_to_dm(0, 0, 100, method='ymw16', nu=1000*u.MHz) assert dist == dist_ assert tau_sc == tau_sc_ dist, tau_sc = pygedm.dist_to_dm(0, 0, 100, method='ne2001', nu=1) dist_, tau_sc_ = pygedm.dist_to_dm(0, 0, 100, method='ne2001', nu=1000*u.MHz) assert dist == dist_ assert tau_sc == tau_sc_ dm, tau_sc_1GHz = pygedm.dm_to_dist(0, 0, 1000, method='ymw16', nu=1.0) dm, tau_sc_100MHz = pygedm.dm_to_dist(0, 0, 1000, method='ymw16', nu=0.1) assert np.isclose(tau_sc_1GHz.value, 0.31681767) assert np.isclose(tau_sc_100MHz.value, 3168.17671061) assert np.isclose((0.1/1.0)**(-4) * tau_sc_1GHz.value, tau_sc_100MHz.value) dm, tau_sc_1GHz = pygedm.dm_to_dist(0, 0, 1000, method='ne2001', nu=1.0) dm, tau_sc_100MHz = pygedm.dm_to_dist(0, 0, 1000, method='ne2001', nu=0.1) assert np.isclose(tau_sc_1GHz.value, 198.57881596) assert np.isclose(tau_sc_100MHz.value, 4988074.33385041)
def test_raises(): """ Test that IGM mode FAILS as expected """ with pytest.raises(RuntimeError): pygedm.dm_to_dist(100, 10, 100, method='ymw1066') with pytest.raises(RuntimeError): pygedm.dist_to_dm(100, 10, 100, method='ne2020') with pytest.raises(RuntimeError): pygedm.calculate_electron_density_xyz(100, 10, 100, method='tc93') with pytest.raises(RuntimeError): pygedm.calculate_electron_density_lbr(100, 10, 100, method='ymwPaleolithic') with pytest.raises(RuntimeError): pygedm.dist_to_dm(100, 10, 100, mode='igm', method='ne2001') with pytest.raises(RuntimeError): pygedm.convert_lbr_to_xyz(0, 0, 0, method='chicken')
def test_igm(): """ Test that IGM mode works as expected Note: tested against YMW16 code with: # CMD: ./ ymw16 -d data -v IGM 204 -6.5 2000 100 1 # OUTPUT: DM_Gal: 252.05 DM_MC: 0.00 DM_IGM: 1647.95 DM_Host: 100.00 # z: 2.311 Dist: 5336.4 log(tau_sc): -2.218 """ dist, tau = pygedm.dm_to_dist(204, -6.5, 2000, dm_host=100, mode='igm') assert np.isclose(dist.value, 5336.4, rtol=0.1) assert np.isclose(np.log10(tau.value), -2.218, rtol=0.1) dm, tau = pygedm.dist_to_dm(204, -6.5, 5336.4, mode='igm') dm_total = dm.value + 252.05 + 100 # Add galactic and host contribution assert np.isclose(dm_total, 2000, rtol=0.1) dm, tau = pygedm.dist_to_dm(204, -6.5, 5336.4 * u.Mpc, mode='igm') dm_total = dm.value + 252.05 + 100 # Add galactic and host contribution assert np.isclose(dm_total, 2000, rtol=0.1)
def calc_dm_galaxy(self, model='ymw16'): """ Calculates the dispersion measure contribution of the Milky Way from either (:attr:`raj`, :attr:`decj`) or (:attr:`gl`, :attr:`gb`). Uses the YMW16 model of the Milky Way free electron column density. Parameters ---------- model : 'ymw16' or 'ne2001', optional The Milky Way dispersion measure model. To use 'ne2001' you will need to install the python port. See https://fruitbat.readthedocs.io/en/latest/user_guide/ne2001_installation.html Default: 'ymw16' Returns ------- :obj:`astropy.units.Quantity` The dispersion measure contribution from the Milky Way of the FRB. """ YMW16_options = ["ymw16", "ne2001"] if model.lower() in YMW16_options: # Since the GEDMs only give a dispersion measure out to a # distance within the galaxy, to get the entire DM contribution of the # galaxy we need to integrate across the full Galaxy max_galaxy_dist = 30 * u.kpc # units: pc # Check to make sure some of the keyword are not None coord_list = [ self.skycoords, self.raj, self.decj, self.gl, self.gb ] if all(val is None for val in coord_list): raise ValueError( """Can not calculate dm_galaxy since coordinates for FRB burst were not provided. Please provide (raj, decj) or (gl, gb) coordinates.""") # Calculate skycoords position if it elif (self.skycoords is None and (self.raj is not None and self.decj is not None) or (self.gl is not None and self.gb is not None)): self._skycoords = self.calc_skycoords() dm_galaxy, tau_sc = pygedm.dist_to_dm(self._skycoords.galactic.l, self._skycoords.galactic.b, max_galaxy_dist, method=model) self.dm_galaxy = dm_galaxy.value self.calc_dm_excess() return self.dm_galaxy
def test_magellanic_cloud(): """ Test that MC mode agrees with YMW16 Note: tested against YMW16 code with: CMD: ./ymw16 -d data -v MC 280.46 -32.88 50000 2 OUTPUT: MC: gl= 280.460 gb= -32.880 Dist= 50000.0 dtest=50000.000000, nstep=10000.000000, dstep=5.000000 DM_Gal: 58.03 DM_MC: 78.87 DM: 136.90 log(tau_sc): -5.399 """ dm, tau = pygedm.dist_to_dm(280.46, -32.88, 50000, mode='mc') assert np.isclose(dm.value, 136.90) assert np.isclose(np.log10(tau.value), -5.399, rtol=0.1)
def test_basic(): """ Basic tests of YMW16 model Note: tested against online NE2001 interface https://www.nrl.navy.mil/rsd/RORF/ne2001/ """ # No access to actual model via web interface #a = pygedm.calculate_electron_density_xyz(1, 2, 3) #assert np.isclose(a.value, 5.220655, atol=0.0001) # FRB180301 value dm, tau = pygedm.dist_to_dm(204, -6.5, 25*u.kpc, method='ne2001') assert np.isclose(dm.value, 150.80, atol=0.01) # Loop through distances and check round trip for dist in (10.*u.pc, 100.*u.pc, 1000.*u.pc): dm, tau = pygedm.dist_to_dm(0, 0, dist, method='ne2001') dist_out, tau = pygedm.dm_to_dist(0, 0, dm, method='ne2001') print(dist, dm, dist_out) assert np.isclose(dist_out.to('pc').value, dist.to('pc').value, rtol=0.1)
def test_basic(): """ Basic tests of YMW16 model Note: tested against online YMW16 interface http://www.atnf.csiro.au/research/pulsar/ymw16/index.php """ a = pygedm.calculate_electron_density_xyz(1, 2, 3) assert np.isclose(a.value, 5.220655, atol=0.0001) a = pygedm.calculate_electron_density_lbr(0, 0, 4000) assert np.isclose(a.value, 0.388407, atol=0.0001) # FRB180301 value dm, tau = pygedm.dist_to_dm(204, -6.5, 25000) assert np.isclose(dm.value, 252.0501, atol=0.01) # Loop through distances and check round trip for dist in (10., 100., 1000.): dm, tau = pygedm.dist_to_dm(0, 0, dist) dist_out, tau = pygedm.dm_to_dist(0, 0, dm.value) assert np.isclose(dist_out.value, dist, rtol=0.1)
def test_frb_calc_dm_galaxy(self): dm_galaxy = self.frb_raj_decj.calc_dm_galaxy() dm_pymw16, t_sc_pymw16 = pygedm.dist_to_dm( self.frb_raj_decj.skycoords.galactic.l, self.frb_raj_decj.skycoords.galactic.b, 30 * u.kpc) assert np.isclose(dm_galaxy.value, dm_pymw16.value) dm_galaxy = self.frb_raj_decj.calc_dm_galaxy(model='ne2001') dm_ne2001, t_sc_ne2001 = pygedm.dist_to_dm( self.frb_raj_decj.skycoords.galactic.l, self.frb_raj_decj.skycoords.galactic.b, 30 * u.kpc, method='ne2001') assert np.isclose(dm_galaxy.value, dm_ne2001.value) dm_galaxy = self.frb_raj_decj.calc_dm_galaxy(model='ymw16') dm_pymw16, t_sc_pymw16 = pygedm.dist_to_dm( self.frb_raj_decj.skycoords.galactic.l, self.frb_raj_decj.skycoords.galactic.b, 30 * u.kpc, method='ymw16') assert np.isclose(dm_galaxy.value, dm_pymw16.value)
import pygedm print("\n--- DM to dist ---") print("YMW16:", pygedm.dm_to_dist(100, 0, 250, dm_host=0, method='ymw16')) print("NE2001:", pygedm.dm_to_dist(100, 0, 250, dm_host=0, method='ne2001')) print("\n--- dist to DM ---") print("YMW16:", pygedm.dist_to_dm(100, 0, 250, method='ymw16')) print("NE2001:", pygedm.dist_to_dm(100, 0, 250, method='ne2001')) print("\n--- Electron density ---") print("YMW16:", pygedm.calculate_electron_density_xyz(0, 0, 0, method='ymw16')) print("NE2001:", pygedm.calculate_electron_density_xyz(0, 0, 0, method='ne2001'))
def calc_dm_galaxy(self, model='ymw16', include_halo=False, return_tau_sc=False): """ Calculates the dispersion measure contribution of the Milky Way from either (:attr:`raj`, :attr:`decj`) or (:attr:`gl`, :attr:`gb`). Uses the YMW16 model of the Milky Way free electron column density. Parameters ---------- model : 'ymw16' or 'ne2001', optional The Milky Way dispersion measure model. To use 'ne2001' you will need to install the python port. See https://fruitbat.readthedocs.io/en/latest/user_guide/ne2001_installation.html Default: 'ymw16' include_halo : bool, optional Include the DM of the galactic halo which isn't included in NE2001 or YMW16. This used the YT2020 halo model. Default: False return_tau_sc : bool, optional Return the scattering timescale in addition to the DM. Default: False Returns ------- dm_galaxy: :obj:`astropy.units.Quantity` The dispersion measure contribution from the Milky Way of the FRB. tau_sc: :obj:`astropy.units.Quantity`, optional The scattering timescale at 1 GHz (s). Only returns if :attr:`return_tau_sc` is `True`. """ YMW16_options = ["ymw16", "YMW16"] NE2001_options = ["ne2001", "NE2001"] if model in YMW16_options: model = 'YMW16' elif model in NE2001_options: model = 'NE2001' else: raise ValueError( "'{}' is not a valid galactic DM model".format(model)) # Check to make sure some of the keyword are not None coord_list = [self.skycoords, self.raj, self.decj, self.gl, self.gb] if all(val is None for val in coord_list): raise ValueError("""Can not calculate dm_galaxy since coordinates for FRB burst were not provided. Please provide (raj, decj) or (gl, gb) coordinates.""") # Calculate skycoords position if it elif (self.skycoords is None and (self.raj is not None and self.decj is not None) or (self.gl is not None and self.gb is not None)): self._skycoords = self.calc_skycoords() dm_galaxy, tau_sc = pygedm.dist_to_dm(gl=self._skycoords.galactic.l, gb=self._skycoords.galactic.b, dist=25000.0, method=model) if include_halo: self.galaxy_halo = pygedm.calculate_halo_dm( gl=self._skycoords.galactic.l, gb=self._skycoords.galactic.b, method='yt2020') else: self.galaxy_halo = 0 * u.pc * u.cm**(-3) self.dm_galaxy_model = model self.dm_galaxy = dm_galaxy.value + self.galaxy_halo.value self.tau_sc = tau_sc.value self.calc_dm_excess() if return_tau_sc: return self.dm_galaxy, self.tau_sc else: return self.dm_galaxy
def find_delta_dm(transient_type, transient_data, ism_model, b_val, mc_deg=5, save_df=True): """ Find pulsar/FRB DMs corrected for by the MW ISM DM and remove observations in complex DM regions. Returns array of DMs FRB data is available as a csv in the FRBs/FRB/frb/data/FRBs repo (FRB catalogue [Petroff et al. 2017]) Pulsar data is avaiable as a csv in the FRBs/pulsars/pulsars/data/atnf_cat repo (v1.61 ATNF pulsar catalogue [Manchester et al. 2005]) Arguments: transient_type (str): Accepts 'frb' or 'pulsar'. transient_data (str): Path to data (in .csv format). ism_model (str): Model used to calculated the MW halo DM. Accepts 'ymw16' [Yao et al. 2017] or 'ne2001' [Cordes & Lazio 2003]. b_val (int): Galactic latitude considered (b>b_val, b<-b_val). mc_deg (int): Number of degrees from Magellanic clouds within which transients are removed. save_df (str, optional): Save transient DMs and coords to csv. Outputs: """ # Sort data and get coords if transient_type == 'frb': transcat_df = pd.read_csv(transient_data, skiprows=1, usecols=[0, 5, 6, 7], names=['Name', 'l', 'b', 'dm']) transcat_df['dm'] = transcat_df['dm'].str.split('&').str[0].astype( float).values coords = SkyCoord(l=transcat_df['l'], b=transcat_df['b'], unit=(u.degree), frame=Galactic) elif transient_type == 'pulsar': transcat_df = pd.read_csv( transient_data, skiprows=2, usecols=[1, 2, 3, 9, 10], names=['Name', 'Pref', 'dm', 'RAJD', 'DECJD']) transcat_df = transcat_df[~transcat_df['dm'].str. contains('*', regex=False)].reset_index( drop=True) transcat_df['dm'] = transcat_df['dm'].astype(float) c_icrs = SkyCoord(ra=transcat_df['RAJD'], dec=transcat_df['DECJD'], unit=(u.degree), frame='icrs') transcat_df['l'] = pd.DataFrame(c_icrs.galactic.l.value) transcat_df['b'] = pd.DataFrame(c_icrs.galactic.b.value) coords = SkyCoord(l=transcat_df['l'], b=transcat_df['b'], unit=(u.degree), frame=Galactic) # Find transients in line of sight of MCs logging.info('Removing transients near Magellanic clouds...') # LMC lmc_distance = 50 * u.kpc lmc_coord = SkyCoord('J052334.6-694522', unit=(u.hourangle, u.deg), distance=lmc_distance) close_to_lmc = lmc_coord.separation(coords) < mc_deg * u.deg lmc_trans = list(transcat_df[close_to_lmc]['Name']) # SMC smc_distance = 61 * u.kpc smc_coord = SkyCoord('J005238.0-724801', unit=(u.hourangle, u.deg), distance=smc_distance) close_to_smc = smc_coord.separation(coords) < mc_deg * u.deg smc_trans = list(transcat_df[close_to_smc]['Name']) transcat_df = transcat_df[~transcat_df['Name']. isin(lmc_trans)].reset_index(drop=True) transcat_df = transcat_df[~transcat_df['Name']. isin(smc_trans)].reset_index(drop=True) if transient_type == 'pulsar': transcat_df = transcat_df[~transcat_df['Pref'].str. contains('mfl+06', regex=False)].reset_index( drop=True) elif transient_type == 'frb': pass # Remove transients with low Galactic lattitudes logging.info('Removing transients with low Galactic lattitudes...') transcat_df = pd.concat([ transcat_df[transcat_df.b > b_val], transcat_df[transcat_df.b < -b_val] ], ignore_index=True) # ISM model logging.info('Correcting transient DMs for ISM...') trans_ism = [] if ism_model == 'ymw16': for i in range(len(transcat_df['dm'])): trans_ism_ = pygedm.dist_to_dm(transcat_df['l'].iloc[i], transcat_df['b'].iloc[i], 100000)[0].value trans_ism = np.append(trans_ism, trans_ism_) elif ism_model == 'ne2001': for i in range(len(transcat_df['dm'])): trans_ism_ = ne.DM(transcat_df['l'].iloc[i], transcat_df['b'].iloc[i], 100.).value trans_ism = np.append(trans_ism, trans_ism_) transcat_df['trans_ism'] = pd.DataFrame(trans_ism) transcat_df['deltaDM'] = pd.DataFrame(transcat_df['dm'] - transcat_df['trans_ism']) if save_df == True: transcat_df.to_csv('transient_data/' + transient_type + 'cat_df_' + ism_model + '_' + str(int(b_val)) + '.csv') logging.info('Transient data saved to csv.') else: pass return np.array(transcat_df['deltaDM'])