Пример #1
0
    def __init__(self, test=False):
        """Initializing."""
        self.test = test
        self.set_file_name()

        # Setup database
        self.db = False
        self.step = 0.1
        self.rounding = 2

        # For parallel processes
        self.temp_path = None

        if self.test:
            self.step = 0.1
            if os.path.exists(self.file_name):
                os.remove(self.file_name)

        if os.path.exists(self.file_name) and self.test is False:
            self.db = True
        else:
            # Calculations take quite some time
            # Provide a way for people to quit
            try:
                self.create_table()
            except KeyboardInterrupt:
                pprint('Losing all progress in calculations')
                os.remove(self.file_name)
                if self.temp:
                    os.remove(self.temp_path)
                sys.exit()
Пример #2
0
    def __init__(self, H_0=67.74, W_m=0.3089, W_v=0.6911, test=False):
        """Initializing."""
        self.H_0 = H_0
        self.W_m = W_m
        self.W_v = W_v
        self.test = test

        self.set_file_name()

        # Setup database
        self.db = False
        self.step = 0.00001
        self.z_max = 6.5

        if self.test:
            self.step = 0.001
            self.z_max = 6.5
            if os.path.exists(self.file_name):
                os.remove(self.file_name)

        if os.path.exists(self.file_name) and self.test is False:
            self.db = True
        else:
            # Calculations take quite some time
            # Provide a way for people to quit
            try:
                self.create_table()
            except KeyboardInterrupt:
                pprint('Losing all progress in calculations')
                os.remove(self.file_name)
                sys.exit()
Пример #3
0
    def check(self, path):
        """Perform checks on path."""
        # Just convient to have files ending in a slash
        if path[-1] != '/':
            path += '/'

        if not os.path.exists(path):
            pprint(f"Creating directory {path}")
            os.makedirs(path)

        return path
Пример #4
0
 def generate(self):
     """Generate all manner of intrinsic parameters."""
     # Let user know what's happening
     pprint(f'Generating {self.name} population')
     self.gen_dist()
     self.gen_direction()
     self.gen_gal_coords()
     self.gen_dm()
     self.gen_w(self.n_gen)
     self.gen_lum(self.n_gen)
     self.gen_si(self.n_gen)
     pprint(f'Finished generating {self.name} population')
Пример #5
0
    def match_surveys(self, interrupt=True):
        """Match up frbs with surveys."""
        # Merge survey names
        surf = os.path.join(self.data_dir, 'paper_survey.csv')
        self._surveys = pd.read_csv(surf)
        cols = ['frb_name', 'pub_description']

        self.df = pd.merge(self.df, self._surveys, on=cols, how='outer')
        # Clean up possible unnamed columns
        self.df = self.df.loc[:, ~self.df.columns.str.contains('unnamed')]

        # Check whether any FRBs have not yet been assigned
        no_surveys = self.df['survey'].isnull()

        if interrupt:
            if any(no_surveys):
                ns_df = self.df[no_surveys]

                pprint('It seems there are new FRBs!')
                pprint(
                    "  - Frbcat doesn't know which *survey* was running when the FRB was seen"
                )
                pprint(
                    "  - To use these recent detections, please link the FRB to a survey by:"
                )
                pprint('  - Adding these frbs to {}'.format(surf))

                for i, r in ns_df[['pub_description', 'frb_name']].iterrows():
                    title, name = r
                    if isinstance(title, str):
                        title = title.replace('\n', '')
                    print(f'"{title}","{name}",""')
Пример #6
0
    def get_data(self):
        """Read in populations."""
        # Read in files
        for f in self.files:

            # Check whether file exists
            if os.path.isfile(f):
                try:
                    df = unpickle(f).frbs.to_df()
                except ValueError:
                    continue
                if '.' in f:
                    name = f.split('/')[-1].split('.')[0]
                else:
                    name = f
            if df is not None:
                pass
            else:
                m = 'Skipping population {} - contains no sources'.format(f)
                pprint(m)
                continue

            # Downsample population size if it's too large
            if df.shape[0] > 10000:
                df = df.iloc[::1000]

            df['population'] = name
            df['color'] = self.colours[self.n_df]
            df['lum_bol'] = df['lum_bol'] / 1e30  # Sidestepping Bokeh issue

            self.dfs.append(df)
            self.n_df += 1

        # Add on frbcat
        if self.frbcat:
            df = Frbcat().df
            # Filter by survey if wished
            if isinstance(self.frbcat, str):
                if df['survey'].str.match(self.frbcat).any():
                    df = df[df.survey == self.frbcat]
                elif df['telescope'].str.match(self.frbcat).any():
                    df = df[df.telescope == self.frbcat]
                else:
                    m = 'Your chosen input for frbcat is not found.'
                    raise ValueError(m)

                df['population'] = f'frbcat {self.frbcat}'

            df['color'] = self.colours[len(self.dfs)]
            self.dfs.append(df)
Пример #7
0
    def get_data(self):
        """Read in populations."""
        # Read in files
        for f in self.files:

            # Check whether file exists
            if os.path.isfile(f):
                try:
                    df = unpickle(f).frbs.to_df()
                except ValueError:
                    pprint(f'Unpacking {f} seemed to have failed.')
                    continue
                if '.' in f:
                    name = f.split('/')[-1].split('.')[0]
                    if '_for_plotting' in name:
                        name = name.split('_for_plotting')[0]
                else:
                    name = f

            # If things haven't worked
            if df is None:
                m = 'Skipping population {} - contains no sources'.format(f)
                pprint(m)
                continue

            # Downsample population size if it's too large
            if df.shape[0] > 10000:
                pprint(f'Downsampling population {f} (else too big to plot)')
                df = df.sample(n=10000)

            df['population'] = name
            df['color'] = self.colours[self.n_df]
            df['lum_bol'] = df['lum_bol'] / 1e30  # Sidestepping Bokeh issue

            if df.empty:
                m = 'Skipping population {} - contains no sources'.format(f)
                pprint(m)
                continue
            else:
                self.dfs.append(df)
                self.n_df += 1

        # Add on frbcat
        if self.frbcat:
            df = Frbcat().df
            # Filter by survey if wished
            if isinstance(self.frbcat, str):
                if df['survey'].str.match(self.frbcat).any():
                    df = df[df.survey == self.frbcat]
                elif df['telescope'].str.match(self.frbcat).any():
                    df = df[df.telescope == self.frbcat]
                else:
                    m = 'Your chosen input for frbcat is not found.'
                    raise ValueError(m)

                df['population'] = f'frbcat {self.frbcat}'

            df['color'] = self.colours[len(self.dfs)]
            self.dfs.append(df)
Пример #8
0
    def to_df(self):
        """Gather source values into a pandas dataframe."""
        values = self._values()

        if not values:
            return None

        if len(values) >= 200000:
            # Take quarter of the values
            pprint(f'Quartering {self.name} population')
            values = values[:len(values) // 4]
            index = values.rfind('\n')
            values = values[:index]

        data = StringIO(values)
        df = pd.read_csv(data)
        return df
Пример #9
0
    def create_table(self):
        """Create a lookup table for dispersion measure."""
        # Connect to database
        conn = sqlite3.connect(self.file_name)
        c = conn.cursor()

        # Set array of coordinates
        gls = np.arange(-180., 180. + self.step, self.step)
        gbs = np.arange(-90., 90. + self.step, self.step)
        dist = 0.1  # [Gpc]

        # Create database
        c.execute('create table dm ' + '(gl real, gb real, dm_mw real)')

        results = []

        # Give an update on the progress
        pprint('Creating a DM lookup table (only needs to be done once)')

        for gl in gls:
            gl = round(gl, 1)
            for gb in gbs:
                gb = round(gb, 1)

                dm_mw = go.ne2001_dist_to_dm(dist, gl, gb)

                r = (gl, gb, dm_mw)
                results.append(r)

            sys.stdout.write('\r{}'.format(gl))
            sys.stdout.flush()

        # Save results to database
        c.executemany('insert into dm values (?,?,?)', results)

        # Make for easier searching
        c.execute('create index ix on dm (gl, gb)')

        # Save
        conn.commit()
Пример #10
0
    def match_surveys(self, interrupt=True):
        """Match up frbs with surveys."""
        # Merge survey names
        surf = os.path.join(self.data_dir, 'paper_survey.csv')
        self._surveys = pd.read_csv(surf)
        cols = ['frb', 'pub_description']
        self.df = pd.merge(self.df, self._surveys, on=cols, how='outer')

        # Clean up possible unnamed columns
        self.df = self.df.loc[:, ~self.df.columns.str.contains('unnamed')]

        # Check whether any FRBs have not yet been assigned
        no_surveys = self.df['survey'].isnull()

        if interrupt:
            if any(no_surveys):
                ns_df = self.df[no_surveys]

                pprint('Please add these frbs to {}'.format(surf))

                for i, r in ns_df[['pub_description', 'frb']].iterrows():
                    title, frb = r
                    print(f'"{title}",{frb},')
Пример #11
0
    def intensity_profile(self, shape=1, dimensions=2, rep_loc='random'):
        """Calculate intensity profile.

        Args:
            shape (tuple): Usually the shape of frbs.s_peak
            dimensions (int): Use a 2D beampattern or a 1D one.
            rep_loc (str): 'same' or 'random'. Whether repeaters are observed
                in the same spot or a different spot

        Returns:
            array, array: intensity profile, offset from beam [arcmin]

        """
        # Calculate Full Width Half Maximum from beamsize
        self.fwhm = 2 * math.sqrt(
            self.beam_size_fwhm / math.pi) * 60  # [arcmin]
        offset = self.fwhm / 2  # Radius = diameter/2.

        if rep_loc == 'same':
            r = r = np.random.random(shape[0])
        else:
            r = np.random.random(shape)

        if dimensions == 2:  # 2D
            offset *= np.sqrt(r)
        elif dimensions == 1:  # 1D
            offset *= r

        # Allow for a perfect beam pattern in which all is detected
        if self.gain_pattern == 'perfect':
            int_pro = np.ones(shape)
            self.beam_size = self.beam_size_fwhm
            return int_pro, offset

        # Formula's based on 'Interferometry and Synthesis in Radio
        # Astronomy' by A. Richard Thompson, James. M. Moran and
        # George W. Swenson, JR. (Second edition), around p. 15

        max_offset = self.max_offset(self.n_sidelobes)
        self.beam_size = math.pi * (self.fwhm / 2 * max_offset /
                                    60)**2  # [sq degrees]

        if self.gain_pattern == 'gaussian':
            # Set the maximum offset equal to the null after a sidelobe
            # I realise this pattern isn't an airy, but you have to cut
            # somewhere
            offset *= max_offset
            alpha = 2 * math.sqrt(math.log(2))
            int_pro = np.exp(-(alpha * offset / self.fwhm)**2)
            return int_pro, offset

        elif self.gain_pattern == 'airy':
            # Set the maximum offset equal to the null after a sidelobe
            offset *= max_offset
            c = 299792458
            conv = math.pi / (60 * 180)  # Conversion arcmins -> radians
            eff_diam = c / (self.central_freq * 1e6 * conv * self.fwhm)
            a = eff_diam / 2  # Effective radius of telescope
            lamda = c / (self.central_freq * 1e6)
            ka = (2 * math.pi * a / lamda)
            kasin = ka * np.sin(offset * conv)
            int_pro = 4 * (j1(kasin) / kasin)**2
            return int_pro, offset

        elif self.gain_pattern in ['parkes', 'apertif']:

            place = paths.models() + f'/beams/{self.gain_pattern}.npy'
            beam_array = np.load(place)
            b_shape = beam_array.shape
            ran_x = np.random.randint(0, b_shape[0], shape)
            ran_y = np.random.randint(0, b_shape[1], shape)
            int_pro = beam_array[ran_x, ran_y]
            offset = np.sqrt((ran_x - b_shape[0] / 2)**2 +
                             (ran_y - b_shape[1] / 2)**2)

            # Scaling factors to correct for pixel scale
            if self.gain_pattern == 'apertif':  # 1 pixel = 0.94'
                offset *= 240 / 256  # [arcmin]
                self.beam_size = 25.
            if self.gain_pattern == 'parkes':  # 1 pixel = 54"
                offset *= 0.9  # [arcmin]
                self.beam_size = 9.

            return int_pro, offset

        else:
            pprint(f'Gain pattern "{self.gain_pattern}" not recognised')
Пример #12
0
def plot(*pops,
         files=[],
         frbcat=True,
         show=True,
         no_browser=False,
         mute=True,
         port=5006,
         print_command=False):
    """
    Plot populations with bokeh. Has to save populations before plotting.

    Args:
        *pops (Population, optional): Add the populations you would like to
            see plotted
        files (list, optional): List of population files to plot.
        frbcat (bool, optional): Whether to plot frbcat parameters. Defaults to
            True
        show (bool, optional): Whether to display the plot or not. Mainly used
            for debugging purposes. Defaults to True.
        port (int): The port on which to launch Bokeh
        print_command (bool): Whether to show the command do_plot is running

    """
    if len(pops) > 0:

        # Save populations
        for pop in pops:

            if type(pop) == str:
                name = pop
            else:
                pop.name = pop.name.lower() + '_for_plotting'
                name = pop.name
                pop.save()

            # Save location
            file_name = name + '.p'
            out = os.path.join(paths.populations(), file_name)
            files.append(out)

    # Command for starting up server
    command = 'nice -n 19'.split(' ')

    if show:
        command.extend('bokeh serve --show'.split(' '))
    else:
        command.append('python3')

    # Command for changing port
    if port != 5006:
        command.append(f'--port={port}')

    # Add script path
    script = 'plot.py'
    out = os.path.join(os.path.dirname(__file__), script)
    command.append(out)

    # For the arguments
    command.append('--args')

    # Add frbcat
    command.append('-frbcat')
    if frbcat is False:
        command.append('False')
    if frbcat is True:
        command.append('True')
    elif type(frbcat) == str and len(frbcat) > 0:
        command.append(f'{frbcat}')

    # Add in populations
    for f in files:
        command.append(f'"{f}"')

    # Let people know what's happening
    pprint('Plotting populations')

    if print_command:
        pprint(' '.join(command))

    pprint('Press Ctrl+C to quit')

    # Add method to gracefully quit plotting
    try:
        with open(os.devnull, 'w') as f:
            if mute:
                out = f
            else:
                out = None
            subprocess.run(command, stderr=out, stdout=out)
    except KeyboardInterrupt:
        print(' ')
        sys.exit()
Пример #13
0
    def create_table(self, parallel=True):
        """Create a lookup table for dispersion measure."""
        # Connect to database
        conn = sqlite3.connect(self.file_name)
        c = conn.cursor()

        # Set array of coordinates
        gls = np.arange(-180., 180. + self.step, self.step).round(1)
        gbs = np.arange(-90., 90. + self.step, self.step).round(1)
        dist = 0.1  # [Gpc]

        gls = gls.astype(np.float32)
        gbs = gbs.astype(np.float32)

        # Create database
        c.execute('create table dm ' + '(gl real, gb real, dm_mw real)')

        # Give an update on the progress
        m = [
            'Creating a DM lookup table', '  - Only needs to happen once',
            '  - Unfortunately pretty slow',
            '  - Prepare to wait for ~1.5h (4 cores)',
            '  - Time given as [time_spent<time_left] in (hh:)mm:ss',
            'Starting to calculate DM values'
        ]
        for n in m:
            pprint(n)

        n_opt = len(gls) * len(gbs)
        options = np.array(np.meshgrid(gls, gbs)).T.reshape(-1, 2)
        dm_mw = np.zeros(len(options)).astype(np.float32)

        def dm_tot(i, dm_mw):
            gl, gb = options[i]
            dm_mw[i] = go.ne2001_dist_to_dm(dist, gl, gb)

        if parallel:

            temp_path = os.path.join(paths.models(), 'universe/') + 'temp.mmap'
            self.temp_path = temp_path

            # Make a temp memmap to have a sharedable memory object
            temp = np.memmap(temp_path,
                             dtype=dm_mw.dtype,
                             shape=len(dm_mw),
                             mode='w+')

            # Parallel process in order to populate array
            r = range(n_opt)
            j = min([4, os.cpu_count() - 1])
            print(os.cpu_count())
            Parallel(n_jobs=j)(delayed(dm_tot)(i, temp) for i in tqdm(r))

            # Map results
            r = np.concatenate((options, temp[:, np.newaxis]), axis=1)
            results = map(tuple, r.tolist())

            # Delete the temporary directory and contents
            try:
                os.remove(temp_path)
            except FileNotFoundError:
                print(f'Unable to remove {temp_path}')

        else:
            for i in tqdm(range(n_opt)):
                dm_tot(i, dm_mw)

            # Save results to database
            dm_mw = dm_mw.astype(np.float32)
            r = np.concatenate((options, dm_mw[:, np.newaxis]), axis=1)
            results = map(tuple, r.tolist())

        pprint('  - Saving results')
        c.executemany('insert into dm values (?,?,?)', results)

        # Make for easier searching
        c.execute('create index ix on dm (gl, gb)')

        # Save
        conn.commit()

        pprint('Finished DM table')
Пример #14
0
    def create_table(self):
        """Create a lookup table for distances."""
        m = [
            'Creating a distance table', '  - Only needs to happen once',
            '  - May take up to 2m on a single core'
        ]
        for n in m:
            pprint(n)

        # Connect to database
        conn = sqlite3.connect(self.file_name)
        c = conn.cursor()

        H_0 = self.H_0
        W_m = self.W_m
        W_v = self.W_v

        W_k = 1.0 - W_m - W_v  # Omega curvature

        if W_k != 0.0:
            pprint('Careful - Your cosmological parameters do not sum to 1.0')

        zs = np.arange(0, self.z_max + self.step, self.step)

        # Create database
        t = 'real'
        par = f'(z {t}, dist {t}, vol {t}, dvol {t}, cdf_sfr {t}, cdf_smd {t})'
        s = f'create table distances {par}'
        c.execute(s)

        results = []

        pprint('  - Calculating parameters at various redshifts')
        conv = go.Redshift(zs, H_0=H_0, W_m=W_m, W_v=W_v)
        dists = conv.dist_co()
        vols = conv.vol_co()

        # Get dV
        dvols = np.zeros_like(vols)
        dvols[1:] = np.diff(vols)

        pprint('  - Calculating Star Formation Rate')
        # Get pdf sfr
        pdf_sfr = sfr(zs) * dvols
        cdf_sfr = np.cumsum(pdf_sfr)  # Unnormalized
        cdf_sfr /= cdf_sfr[-1]

        pprint('  - Calculating Stellar Mass Density')
        # Get pdf csmd
        pdf_smd = smd(zs, H_0=H_0, W_m=W_m, W_v=W_v) * dvols
        cdf_smd = np.cumsum(pdf_smd)  # Unnormalized
        cdf_smd /= cdf_smd[-1]

        results = np.stack((zs, dists, vols, dvols, cdf_sfr, cdf_smd)).T

        pprint('  - Saving values to database')
        # Save results to database
        data = map(tuple, results.tolist())
        c.executemany('insert into distances values (?,?,?,?,?,?)', data)

        # Make for easier searching
        # I don't really understand SQL index names...
        c.execute('create index ix on distances (z)')
        c.execute('create index ixx on distances (dist)')
        c.execute('create index ixxx on distances (vol)')
        c.execute('create index ixxxx on distances (dvol)')
        c.execute('create index ixxxxx on distances (cdf_sfr)')
        c.execute('create index ixxxxxx on distances (cdf_smd)')

        # Save
        conn.commit()

        pprint('Finished distance table')
Пример #15
0
    def get(self, internet=True, save=True, local=False):
        """
        Get frbcat from online or from a local file.

        Args:
            internet (bool): Whether to get a new version of frbcat
            local (bool): Whether to use a local version of frbcat

        """
        # Check whether a copy of FRBCAT has already been downloaded
        # Ensures frbcat is only queried once a month
        path = self.data_dir + '/frbcat_'
        path += str(datetime.datetime.today()).split()[0][:-3]
        path += '-??.csv'
        exists = glob.glob(path)
        if internet and exists:
            internet = False
            local = True

        if internet:
            try:
                pprint('Attempting to retrieve FRBCAT from www.frbcat.org')

                # First get all FRB names from the main page
                pprint(' - Getting FRB names')
                url = 'http://frbcat.org/products/'
                main_df = self.url_to_df(url)

                # Then get any subsequent analyses (multiple entries per FRB)
                pprint(' - Getting subsequent analyses')
                url = 'http://frbcat.org/product/'
                frb_df = self.urls_to_df(main_df.frb_name, url)

                # Find all frb note properties
                pprint(' - Getting notes on FRBs')
                url = 'http://frbcat.org/frbnotes/'
                frbnotes_df = self.urls_to_df(set(frb_df.index), url)
                if frbnotes_df is not None:
                    frbnotes_df = frbnotes_df.add_prefix('frb_notes_')

                # Find all notes on radio observation parameters
                pprint(' - Getting radio observation parameters')
                url = 'http://frbcat.org/ropnotes/'
                ropnotes_df = self.urls_to_df(set(frb_df.index), url)
                if ropnotes_df is not None:
                    ropnotes_df = ropnotes_df.add_prefix('rop_notes_')

                # Find all radio measurement parameters
                pprint(' - Getting radio measurement parameters')
                url = 'http://frbcat.org/rmppubs/'
                rmppubs_df = self.urls_to_df(set(frb_df.index), url)
                rmppubs_df = rmppubs_df.add_prefix('rmp_pub_')

                # Have skipped
                # 'http://frbcat.org/rmpimages/<rmp_id>' (images)
                # 'http://frbcat.org/rmpnotes/<rmp_id>' (empty)

                # Merge all databases together
                try:
                    df = pd.merge(frb_df,
                                  frbnotes_df,
                                  left_on='frb_id',
                                  right_on='frb_notes_frb_id',
                                  how='left')
                except ValueError:
                    df = frb_df

                df = pd.merge(df,
                              ropnotes_df,
                              left_on='rop_id',
                              right_on='rop_notes_rop_id',
                              how='left')

                self.df = pd.merge(df,
                                   rmppubs_df,
                                   left_on='rmp_id',
                                   right_on='rmp_pub_rmp_id',
                                   how='left')

                pprint('Succeeded')

                if save:
                    date = str(datetime.datetime.today()).split()[0]
                    path = self.data_dir + f'/frbcat_{date}.csv'
                    self.df.to_csv(path)

                local = False

            # Unless there's no internet
            except requests.ConnectionError:
                local = True

        if local or not internet:
            # Find latest version of frbcat
            f = max(glob.glob(self.data_dir + '/frbcat*.csv'),
                    key=os.path.getctime)
            pprint(f"Using {f.split('/')[-1]}")
            self.df = pd.read_csv(f)
Пример #16
0
    def __init__(self,
                 cosmic_pop,
                 survey,
                 scat=False,
                 scin=False,
                 rate_limit=True):
        """
        Run a survey to detect FRB sources.

        Args:
            cosmic_pop (Population): Population class of FRB sources to observe
            survey (Survey): Survey class with which to observe
            scat (bool, optional): Whether to include scattering in signal to
                noise calculations.
            scin (bool, optional): Whether to apply scintillation to
                observations.
            rate_limit (bool, optional): Whether to limit detections by 1/(1+z)
                due to limitation in observing time
        """
        # Set up population
        Population.__init__(self)

        # Set attributes
        self.name = survey.name
        self.time = cosmic_pop.time
        self.vol_co_max = cosmic_pop.vol_co_max
        self.frbs = deepcopy(cosmic_pop.frbs)
        self.rate = Rates()

        pprint(f'Surveying {cosmic_pop.name} with {self.name}')

        frbs = self.frbs

        # Check whether source is in region
        region_mask = survey.in_region(frbs)
        frbs.apply(region_mask)
        self.rate.out = np.size(region_mask) - np.count_nonzero(region_mask)

        # Calculate dispersion measure across single channel
        frbs.t_dm = survey.dm_smear(frbs)

        # Set scattering timescale
        if scat:
            frbs.t_scat = survey.calc_scat(frbs.dm)

        # Calculate total temperature
        frbs.T_sky, frbs.T_sys = survey.calc_Ts(frbs)

        # Calculate effective pulse width
        frbs.w_eff = survey.calc_w_eff(frbs)

        # Calculate peak flux density
        f_min = cosmic_pop.f_min
        f_max = cosmic_pop.f_max
        frbs.s_peak = survey.calc_s_peak(frbs, f_low=f_min, f_high=f_max)

        # Account for beam offset
        int_pro, _ = survey.intensity_profile(n_gen=len(frbs.s_peak))
        frbs.s_peak *= int_pro

        # Calculate fluence [Jy*ms]
        frbs.fluence = frbs.s_peak * frbs.w_eff

        # Caculate Signal to Noise Ratio
        frbs.snr = survey.calc_snr(frbs)

        # Add scintillation
        if scin:
            frbs.snr = survey.calc_scint(frbs)

        # Check whether frbs would be above detection threshold
        snr_mask = (frbs.snr >= survey.snr_limit)
        frbs.apply(snr_mask)
        self.rate.faint = np.size(snr_mask) - np.count_nonzero(snr_mask)

        if rate_limit is True:
            limit = 1 / (1 + frbs.z)
            rate_mask = np.random.random(len(frbs.z)) <= limit
            frbs.apply(rate_mask)
            self.rate.late = np.size(rate_mask) - np.count_nonzero(rate_mask)

        self.rate.det = len(frbs.snr)

        # Calculate scaling factors for rates
        area_sky = 4 * math.pi * (180 / math.pi)**2  # In sq. degrees
        f_area = (survey.beam_size * self.rate.tot())
        inside = self.rate.det + self.rate.late + self.rate.faint
        f_area /= (inside * area_sky)
        f_time = 86400 / self.time

        # Saving scaling factors
        self.rate.days = self.time / 86400  # seconds -> days
        self.rate.f_area = f_area
        self.rate.f_time = f_time
        self.rate.name = self.name
        self.rate.vol = self.rate.tot()
        self.rate.vol /= self.vol_co_max * (365.25 * 86400 / self.time)
Пример #17
0
        curdoc().title = 'frbpoppy'
        curdoc().add_root(L)


# Parse system arguments
# (I know ArgumentParser is nicer, but bokeh only works with argv)
args = sys.argv

# Whether to plot the frbcat population
if '-frbcat' in args:
    frbcat = args[args.index('-frbcat') + 1]
    if frbcat == 'True':
        frbcat = True
    elif frbcat == 'False':
        frbcat = False
else:
    frcat = True

# Which files to plot
files = []
for a in args:
    a = a.strip('"')
    if a.endswith('.p'):
        files.append(a)

# Check whether populations have been given as input
if len(files) == 0:
    pprint('Nothing to plot: plot arguments are empty')
else:
    Plot(files=files, frbcat=frbcat)
Пример #18
0
    def __init__(self,
                 cosmic_pop,
                 survey,
                 scat=False,
                 scin=False,
                 rate_limit=True):
        """
        Run a survey to detect FRB sources.

        Args:
            cosmic_pop (Population): Population class of FRB sources to observe
            survey (Survey): Survey class with which to observe
            scat (bool, optional): Whether to include scattering in signal to
                noise calculations.
            scin (bool, optional): Whether to apply scintillation to
                observations.
            rate_limit (bool, optional): Whether to limit detections by 1/(1+z)
                due to limitation in observing time
        """
        pprint(f'Surveying {cosmic_pop.name} with {survey.name}')
        # Stops RuntimeWarnings about nan values
        np.warnings.filterwarnings('ignore')

        # Set up population
        Population.__init__(self)

        # Set attributes
        self.name = survey.name
        self.time = cosmic_pop.time
        self.vol_co_max = cosmic_pop.vol_co_max
        self.frbs = deepcopy(cosmic_pop.frbs)
        self.rate = Rates()

        frbs = self.frbs

        # Check whether source is in region
        region_mask = survey.in_region(frbs)
        frbs.apply(region_mask)
        self.rate.out = np.size(region_mask) - np.count_nonzero(region_mask)

        if survey.pointings:
            p_mask = survey.in_pointings(frbs)
            frbs.apply(p_mask)
            self.rate.out += np.size(p_mask) - np.count_nonzero(p_mask)

        # Calculate dispersion measure across single channel
        frbs.t_dm = survey.dm_smear(frbs)

        # Set scattering timescale
        if scat:
            frbs.t_scat = survey.calc_scat(frbs.dm)

        # Calculate total temperature
        frbs.T_sky, frbs.T_sys = survey.calc_Ts(frbs)

        # Calculate effective pulse width
        frbs.w_eff = survey.calc_w_eff(frbs)

        # Calculate peak flux density
        f_min = cosmic_pop.f_min
        f_max = cosmic_pop.f_max
        frbs.s_peak = survey.calc_s_peak(frbs, f_low=f_min, f_high=f_max)

        # Account for beam offset
        int_pro, offset = survey.intensity_profile(shape=frbs.s_peak.shape)
        if int_pro.ndim == frbs.s_peak.ndim:
            frbs.s_peak *= int_pro
        else:
            frbs.s_peak *= int_pro[:, None]
        frbs.offset = offset / 60.  # In degrees

        # Calculate fluence [Jy*ms]
        frbs.fluence = survey.calc_fluence(frbs)

        # Caculate Signal to Noise Ratio
        frbs.snr = survey.calc_snr(frbs)

        # Add scintillation
        if scin:
            # Not sure whether this can cope with 2D arrays
            frbs.snr = survey.calc_scint(frbs)

        # Check whether frbs would be above detection threshold
        snr_mask = (frbs.snr >= survey.snr_limit)
        frbs.apply(snr_mask)
        self.rate.faint = np.size(snr_mask) - np.count_nonzero(snr_mask)

        if rate_limit is True:
            limit = 1 / (1 + frbs.z)
            rate_mask = np.random.random(len(frbs.z)) <= limit
            frbs.apply(rate_mask)
            self.rate.late = np.size(rate_mask) - np.count_nonzero(rate_mask)

        self.rate.det = len(frbs.snr)

        self.calc_rates(survey)
Пример #19
0
    def __init__(self,
                 n_gen,
                 days=1,
                 name='cosmic',
                 H_0=67.74,
                 W_m=0.3089,
                 W_v=0.6911,
                 dm_host_model='normal',
                 dm_host_mu=100,
                 dm_host_sigma=200,
                 dm_igm_index=1000,
                 dm_igm_sigma=None,
                 dm_mw_model='ne2001',
                 emission_range=[10e6, 10e9],
                 lum_range=[1e40, 1e45],
                 lum_index=0,
                 n_model='sfr',
                 alpha=-1.5,
                 pulse_model='lognormal',
                 pulse_range=[0.1, 10],
                 pulse_mu=0.1,
                 pulse_sigma=0.5,
                 si_mu=-1.4,
                 si_sigma=1.,
                 z_max=2.5):
        """Generate a popuation of FRBs.

        Args:
            n_gen (int): Number of FRB sources/sky/time to generate.
            days (float): Number of days over which FRBs are generated.
            name (str): Population name.
            H_0 (float): Hubble constant.
            W_m (float): Density parameter Ω_m.
            W_v (float): Cosmological constant Ω_Λ.
            dm_host_model (float): Dispersion measure host model. Options are
                'normal' or 'lognormal'.
            dm_host_mu (float): Mean dispersion measure host [pc/cm^3].
            dm_host_sigma (float): Deviation dispersion measure host [pc/cm^3].
            dm_igm_index (float): Dispersion measure slope for IGM [pc/cm^3].
            dm_igm_sigma (float): Scatter around dm_igm. Defaults 0.2*slope*z
            dm_mw_model (str): Dispersion measure model for the Milky Way.
                Options are 'ne2001' or 'zero'.
            emission_range (list): The frequency range [Hz] between which FRB
                sources should emit the given bolometric luminosity.
            lum_range (list): Bolometric luminosity (distance) range [erg/s].
            lum_index (float): Power law index.
            n_model (str): Number density model. Either 'vol_co', 'sfr' or
                'smd'.
            alpha (float): Desired logN/logS of perfectly detected population.
            pulse_model (str): Pulse width model, 'lognormal' or 'uniform'.
            pulse_range (list): Pulse width range [ms].
            pulse_mu (float): Mean pulse width [ms].
            pulse_sigma (float): Deviation pulse width [ms].
            si_mu (float): Mean spectral index.
            si_sigma (float): Standard deviation spectral index.
            z_max (float): Maximum redshift.

        Returns:
            Population: Population of FRBs.

        """
        # Set up population
        Population.__init__(self)
        self.alpha = alpha
        self.dm_host_model = dm_host_model
        self.dm_host_mu = dm_host_mu
        self.dm_host_sigma = dm_host_sigma
        self.dm_igm_index = dm_igm_index
        self.dm_igm_sigma = dm_igm_sigma
        self.dm_mw_model = dm_mw_model
        self.f_max = emission_range[1]
        self.f_min = emission_range[0]
        self.H_0 = H_0
        self.lum_max = lum_range[1]
        self.lum_min = lum_range[0]
        self.lum_pow = lum_index
        self.name = name
        self.n_gen = n_gen
        self.n_model = n_model
        self.si_mu = si_mu
        self.si_sigma = si_sigma
        self.time = days * 86400  # Convert to seconds
        self.w_model = pulse_model
        self.w_max = pulse_range[1]
        self.w_min = pulse_range[0]
        self.w_mu = pulse_mu
        self.w_sigma = pulse_sigma
        self.W_m = W_m
        self.W_v = W_v
        self.z_max = z_max

        # Cosmology calculations
        r = go.Redshift(self.z_max, H_0=self.H_0, W_m=self.W_m, W_v=self.W_v)
        self.dist_co_max = r.dist_co()
        self.vol_co_max = r.vol_co()

        # Ensure precalculations are done if necessary
        pc.DistanceTable(H_0=self.H_0, W_m=self.W_m, W_v=self.W_v)

        # Set up number density
        n_den = NumberDensity(model=self.n_model,
                              z_max=self.z_max,
                              alpha=self.alpha,
                              H_0=self.H_0,
                              W_m=self.W_m,
                              W_v=self.W_v).draw

        # Let user know what's happening
        pprint(f'Generating {self.name} population')

        frbs = self.frbs

        # Add random directional coordinates
        frbs.gl = np.random.random(n_gen) * 360.0 - 180
        frbs.gb = np.degrees(np.arcsin(np.random.random(n_gen)))
        frbs.gb[::2] *= -1

        # Convert
        frbs.ra, frbs.dec = go.lb_to_radec(frbs.gl, frbs.gb)

        # Draw from number density
        frbs.z, frbs.dist_co = n_den(n_gen)

        # Get the proper distance
        dist_pr = frbs.dist_co / (1 + frbs.z)

        # Convert into galactic coordinates
        frbs.gx, frbs.gy, frbs.gz = go.lb_to_xyz(frbs.gl, frbs.gb, dist_pr)

        # Dispersion measure of the Milky Way
        if self.dm_mw_model == 'ne2001':
            frbs.dm_mw = pc.NE2001Table().lookup(frbs.gl, frbs.gb)
        elif self.dm_mw_model == 'zero':
            frbs.dm_mw = np.zeros_like(frbs.z)

        # Dispersion measure of the intergalactic medium
        frbs.dm_igm = go.ioka_dm_igm(frbs.z,
                                     slope=self.dm_igm_index,
                                     sigma=self.dm_igm_sigma)

        # Dispersion measure of the host (Tendulkar)
        if self.dm_host_model == 'normal':
            frbs.dm_host = dis.trunc_norm(self.dm_host_mu, self.dm_host_sigma,
                                          n_gen).astype(np.float64)
        elif self.dm_host_model == 'lognormal':
            frbs.dm_host = np.random.lognormal(self.dm_host_mu,
                                               self.dm_host_sigma,
                                               n_gen).astype(np.float64)

        frbs.dm_host /= (1 + frbs.z)

        # Total dispersion measure
        frbs.dm = frbs.dm_mw + frbs.dm_igm + frbs.dm_host

        # Get a random intrinsic pulse width [ms]
        if self.w_model == 'lognormal':
            frbs.w_int = np.random.lognormal(self.w_mu, self.w_sigma, n_gen)

        if self.w_model == 'uniform':
            frbs.w_int = np.random.uniform(self.w_min, self.w_max, n_gen)

        # Calculate the pulse width upon arrival to Earth
        frbs.w_arr = frbs.w_int * (1 + frbs.z)

        # Add bolometric luminosity [erg/s]
        frbs.lum_bol = dis.powerlaw(self.lum_min, self.lum_max, self.lum_pow,
                                    n_gen)

        # Add spectral index
        frbs.si = np.random.normal(si_mu, si_sigma, n_gen)

        pprint('Finished')