Exemplo n.º 1
0
    def sample_ecc(self, ecc_model='sana12', size=None):
        """Sample the eccentricity according to a user specified model

        Parameters
        ----------
        ecc_model : string
            'thermal' samples from a  thermal eccentricity distribution following
            `Heggie (1975) <http://adsabs.harvard.edu/abs/1975MNRAS.173..729H>`_
            'uniform' samples from a uniform eccentricity distribution
            'sana12' samples from the eccentricity distribution from `Sana+2012 <https://ui.adsabs.harvard.edu/abs/2012Sci...337..444S/abstract>_`
            'circular' assumes zero eccentricity for all systems
            DEFAULT = 'sana12'

        size : int, optional
            number of eccentricities to sample
            NOTE: this is set in cosmic-pop call as Nstep

        Returns
        -------
        ecc : array
            array of sampled eccentricities with size=size
        """

        if ecc_model=='thermal':
            a_0 = np.random.uniform(0.0, 1.0, size)
            ecc = a_0**0.5
            return ecc

        elif ecc_model=='uniform':
            ecc = np.random.uniform(0.0, 1.0, size)
            return ecc

        elif ecc_model=='sana12':
            from cosmic.utils import rndm
            ecc = rndm(a=0.001, b=0.9, g=-0.45, size=size) 
            return ecc

        elif ecc_model=='circular':
            ecc = np.zeros(size)
            return ecc

        else:
            raise Error('You have specified an unsupported model. Please choose from thermal, uniform, sana12, or circular')
Exemplo n.º 2
0
    def sample_primary(self, primary_model='kroupa93', size=None):
        """Sample the primary mass (always the most massive star) from a user-selected model

        kroupa93 follows Kroupa (1993), normalization comes from
        `Hurley 2002 <https://arxiv.org/abs/astro-ph/0201220>`_
        between 0.08 and 150 Msun
        salpter55 follows
        `Salpeter (1955) <http://adsabs.harvard.edu/abs/1955ApJ...121..161S>`_
        between 0.08 and 150 Msun
        kroupa01 follows Kroupa (2001) <https://arxiv.org/abs/astro-ph/0009005>
        between 0.08 and 100 Msun


        Parameters
        ----------
        primary_model : str, optional
            model for mass distribution; choose from:

            kroupa93 follows Kroupa (1993), normalization comes from
            `Hurley 2002 <https://arxiv.org/abs/astro-ph/0201220>`_
            valid for masses between 0.1 and 100 Msun


            salpter55 follows
            `Salpeter (1955) <http://adsabs.harvard.edu/abs/1955ApJ...121..161S>`_
            valid for masses between 0.1 and 100 Msun

            kroupa01 follows Kroupa (2001), normalization comes from
            `Hurley 2002 <https://arxiv.org/abs/astro-ph/0009005>`_
            valid for masses between 0.1 and 100 Msun

            Default kroupa93
        size : int, optional
            number of initial primary masses to sample
            NOTE: this is set in cosmic-pop call as Nstep

        Returns
        -------
        a_0 : array
            Sampled primary masses
        sampled_mass : float
            Total amount of mass sampled
        """

        if primary_model == 'kroupa93':
            total_sampled_mass = 0
            multiplier = 1
            a_0 = np.random.uniform(0.0, 1, size)

            low_cutoff = 0.740074
            high_cutoff = 0.908422

            lowIdx, = np.where(a_0 <= low_cutoff)
            midIdx, = np.where((a_0 > low_cutoff) & (a_0 < high_cutoff))
            highIdx, = np.where(a_0 >= high_cutoff)

            a_0[lowIdx] = rndm(a=0.08, b=0.5, g=-0.3, size=len(lowIdx))
            a_0[midIdx] = rndm(a=0.50, b=1.0, g=-1.2, size=len(midIdx))
            a_0[highIdx] = rndm(a=1.0, b=150.0, g=-1.7, size=len(highIdx))

            total_sampled_mass += np.sum(a_0)

            return a_0, total_sampled_mass

        elif primary_model == 'kroupa01':
            total_sampled_mass = 0
            multiplier = 1
            a_0 = np.random.uniform(0.0, 1, size)

            low_cutoff = 0.37148816884988606
            high_cutoff = 0.8496015751162523

            lowIdx, = np.where(a_0 <= low_cutoff)
            midIdx, = np.where((a_0 > low_cutoff) & (a_0 < high_cutoff))
            highIdx, = np.where(a_0 >= high_cutoff)

            a_0[lowIdx] = rndm(a=0.01, b=0.08, g=0.7, size=len(lowIdx))
            a_0[midIdx] = rndm(a=0.08, b=0.5, g=-0.3, size=len(midIdx))
            a_0[highIdx] = rndm(a=0.5, b=100.0, g=-1.3, size=len(highIdx))

            total_sampled_mass += np.sum(a_0)

            return a_0, total_sampled_mass

        elif primary_model == 'salpeter55':
            total_sampled_mass = 0
            multiplier = 1
            a_0 = rndm(a=0.08, b=150, g=-1.35, size=size * multiplier)

            total_sampled_mass += np.sum(a_0)

            return a_0, total_sampled_mass
Exemplo n.º 3
0
    def sample_primary(self, primary_min, primary_max, primary_model='kroupa93', size=None):
        """Sample the primary mass (always the most massive star) from a user-selected model

        kroupa93 follows Kroupa (1993), normalization comes from
        `Hurley 2002 <https://arxiv.org/abs/astro-ph/0201220>`_
        between 0.1 and 100 Msun
        salpter55 follows
        `Salpeter (1955) <http://adsabs.harvard.edu/abs/1955ApJ...121..161S>`_
        between 0.1 and 100 Msun

        Parameters
        ----------
        primary_min : float
            minimum initial primary mass [Msun]
            
        primary_max : float
            maximum initial primary mass [Msun]
            
        primary_model : str, optional
            model for mass distribution; choose from:
                
            kroupa93 follows Kroupa (1993), normalization comes from
            `Hurley 2002 <https://arxiv.org/abs/astro-ph/0201220>`_
            valid for masses between 0.1 and 100 Msun
            
            salpter55 follows
            `Salpeter (1955) <http://adsabs.harvard.edu/abs/1955ApJ...121..161S>`_
            valid for masses between 0.1 and 100 Msun
                
            Default kroupa93
        size : int, optional                
            number of initial primary masses to sample
            NOTE: this is set in runFixedPop call as Nstep

        Returns
        -------
        a_0 : array
            Sampled primary masses
        total_sampled_mass : float
            Total amount of mass sampled
        """

        if primary_model=='kroupa93':
            # If the final binary contains a compact object (BH or NS),
            # we want to evolve 'size' binaries that could form a compact
            # object so we over sample the initial population
            if primary_max >= 150.0:
                a_0 = np.random.uniform(0.0, 0.9999797, size*500)
            elif primary_max >= 30.0:
                a_0 = np.random.uniform(0.0, 0.9999797, size*50)
            else:
                a_0 = np.random.uniform(0.0, 0.9999797, size)

            low_cutoff = 0.740074
            high_cutoff=0.908422

            lowIdx, = np.where(a_0 <= low_cutoff)
            midIdx, = np.where((a_0 > low_cutoff) & (a_0 < high_cutoff))
            highIdx, = np.where(a_0 >= high_cutoff)

            a_0[lowIdx] = ((0.1) ** (-3.0/10.0) - (a_0[lowIdx] / 0.968533)) ** (-10.0/3.0)
            a_0[midIdx] = ((0.5) ** (-6.0/5.0) - ((a_0[midIdx] - low_cutoff) / 0.129758)) ** (-5.0/6.0)
            a_0[highIdx] = (1 - ((a_0[highIdx] - high_cutoff) / 0.0915941)) ** (-10.0/17.0)

            total_sampled_mass = np.sum(a_0)

            a_0 = a_0[a_0 >= primary_min]
            a_0 = a_0[a_0 <= primary_max]
            return a_0, total_sampled_mass

        elif primary_model=='salpeter55':
            # If the final binary contains a compact object (BH or NS),
            # we want to evolve 'size' binaries that could form a compact
            # object so we over sample the initial population
            if primary_max == 150.0:
                a_0 = rndm(a=0.1, b=100, g=-1.35, size=size*500)
            elif primary_max == 50.0:
                a_0 = rndm(a=0.1, b=100, g=-1.35, size=size*50)
            else:
                a_0 = rndm(a=0.1, b=100, g=-1.35, size=size)

            total_sampled_mass = np.sum(a_0)

            a_0 = a_0[a_0 >= primary_min]
            a_0 = a_0[a_0 <= primary_max]

            return a_0, total_sampled_mass
Exemplo n.º 4
0
    def sample_porb(self, mass1, mass2, ecc, porb_model='sana12', size=None):
        """Sample the orbital period according to the user-specified model
        
        Parameters
        ----------
        mass1 : array
            primary masses
        mass2 : array
            secondary masses
        ecc : array
            eccentricity
        model : string
            selects which model to sample orbital periods, choices include:
            log_uniform : semi-major axis flat in log space from RRLO < 0.5 up
                       to 1e5 Rsun according to
                       `Abt (1983) <http://adsabs.harvard.edu/abs/1983ARA%26A..21..343A>`_
                        and consistent with Dominik+2012,2013
                        and then converted to orbital period in days using Kepler III
            sana12 : power law orbital period between 0.15 < log(P/day) < 5.5 following 
                        `Sana+2012 <https://ui.adsabs.harvard.edu/abs/2012Sci...337..444S/abstract>_` 
            renzo19 : power law orbital period for m1 > 15Msun binaries from 
                        `Sana+2012 <https://ui.adsabs.harvard.edu/abs/2012Sci...337..444S/abstract>_` 
                        following the implementation of 
                        `Renzo+2019 <https://ui.adsabs.harvard.edu/abs/2019A%26A...624A..66R/abstract>_` and flat in log otherwise

        Returns
        -------
        porb : array
            orbital period with array size equalling array size
            of mass1 and mass2 in units of days
        """
        if porb_model == 'log_uniform':     
            q = mass2/mass1
            RL_fac = (0.49*q**(2./3.)) / (0.6*q**(2./3.) + np.log(1+q**1./3.))

            q2 = mass1/mass2
            RL_fac2 = (0.49*q2**(2./3.)) / (0.6*q2**(2./3.) + np.log(1+q2**1./3.))
            try:
                ind_lo, = np.where(mass1 < 1.66)
                ind_hi, = np.where(mass1 >= 1.66)

                rad1 = np.zeros(len(mass1))
                rad1[ind_lo] = 1.06*mass1[ind_lo]**0.945
                rad1[ind_hi] = 1.33*mass1[ind_hi]**0.555
            except:
                if mass1 < 1.66:
                    rad1 = 1.06*mass1**0.945
                else:
                    rad1 = 1.33*mass1**0.555

            try:
                ind_lo, = np.where(mass2 < 1.66)
                ind_hi, = np.where(mass2 >= 1.66)

                rad2 = np.zeros(len(mass2))
                rad2[ind_lo] = 1.06*mass2[ind_lo]**0.945
                rad2[ind_hi] = 1.33*mass2[ind_hi]**0.555
            except:
                if mass2 < 1.66:
                    rad2 = 1.06*mass1**0.945
                else:
                    rad2 = 1.33*mass1**0.555

            # include the factor for the eccentricity
            RL_max = 2*rad1/RL_fac
            ind_switch, = np.where(RL_max < 2*rad2/RL_fac2)
            if len(ind_switch) >= 1:
                RL_max[ind_switch] = 2*rad2/RL_fac2[ind_switch]
            a_min = RL_max*(1+ecc)
            a_0 = np.random.uniform(np.log(a_min), np.log(1e5), size)

            # convert out of log space
            a_0 = np.exp(a_0)
            # convert to au
            rsun_au = 0.00465047
            a_0 = a_0*rsun_au

            # convert to orbital period in years
            yr_day = 365.24
            porb_yr = ((a_0**3.0)/(mass1+mass2))**0.5
            porb = porb_yr*yr_day
        elif porb_model == 'sana12':
            from cosmic.utils import rndm
            porb = 10**rndm(a=0.15, b=5.5, g=-0.55, size=size)
        elif porb_model == 'renzo19':
            from cosmic.utils import rndm
            porb = 10**(np.random.uniform(0.15, 5.5, size))
            ind_massive, = np.where(mass1 > 15)
            porb[ind_massive] = 10**rndm(a=0.15, b=5.5, g=-0.55, size=len(ind_massive))
        else:
            raise ValueError('You have supplied a non-supported model; Please choose either log_flat, sana12, or renzo19')
        return porb