Example #1
0
    def __init__(self,
                 age=3.9e6,
                 ext=2.63,
                 dist=7.971e3,
                 met=0.0,
                 phase=None,
                 use_atm_func='merged'):
        log_age = np.log10(age)

        self.log_age = log_age
        self.A_Ks = ext
        self.dist = dist
        self.met = met

        # Evolution/Atmosphere Models
        evo_model = evolution.MISTv1()

        if use_atm_func == 'merged':
            atm_func = atmospheres.get_merged_atmosphere
        elif use_atm_func == 'phoenix':
            atm_func = atmospheres.get_phoenixv16_atmosphere

        # Extinction law
        red_law = reddening.RedLawNoguerasLara18()
        self.ext_alpha = 2.30

        ## Calculate extinctions implied by isochrone extinction
        self.A_B = self.A_Ks * (lambda_Ks / lambda_B)**self.ext_alpha
        self.A_R = self.A_Ks * (lambda_Ks / lambda_R)**self.ext_alpha

        # Create an isochhrone with the given parameters
        self.iso_curAge = synthetic.IsochronePhot(self.log_age,
                                                  self.A_Ks,
                                                  self.dist,
                                                  evo_model=evo_model,
                                                  atm_func=atm_func,
                                                  red_law=red_law,
                                                  metallicity=self.met,
                                                  filters=self.filt_list)

        # Save out specific stellar parameter columns needed
        ## If needing specific phase, draw it out before saving
        if phase is not None:
            phase_check = np.where(
                self.iso_curAge.points['phase'] == mist_phase_dict[phase])
        else:
            phase_check = np.where(self.iso_curAge.points['phase'] >= -1)

        self.iso_mass_init = self.iso_curAge.points['mass'][phase_check]
        self.iso_mass = self.iso_curAge.points['mass_current'][phase_check]
        self.iso_rad = (self.iso_curAge.points['R'][phase_check]).to(u.solRad)
        self.iso_lum = self.iso_curAge.points['L'][phase_check]
        self.iso_teff = self.iso_curAge.points['Teff'][phase_check]

        self.iso_mag_B = self.iso_curAge.points['m_ubv_B'][phase_check]
        self.iso_mag_R = self.iso_curAge.points['m_ubv_R'][phase_check]

        self.iso_rad_min = np.min(self.iso_rad).value
        self.iso_rad_max = np.max(self.iso_rad).value
Example #2
0
def test_evolution_models():
    """
    Test to make sure the different evolution models work
    """
    from popstar import evolution

    # Age ranges to test
    age_young_arr = [6.7, 7.9]
    age_all_arr = [6.7, 8.0, 9.7]

    # Metallicity ranges to test (if applicable)
    metal_range = [-2.5, 0, 0.4]
    metal_solar = [0]

    # Array of evolution models to test
    evo_models = [
        evolution.MISTv1(version=1.2),
        evolution.MergedBaraffePisaEkstromParsec(),
        evolution.MergedSiessGenevaPadova(),
        evolution.Parsec(),
        evolution.Baraffe15(),
        evolution.Ekstrom12(),
        evolution.Pisa()
    ]

    # Array of age_ranges for the specific evolution models to test
    age_vals = [
        age_all_arr, age_all_arr, age_all_arr, age_all_arr, age_young_arr,
        age_young_arr, age_young_arr
    ]

    # Array of metallicities for the specific evolution models to test
    metal_vals = [
        metal_range, metal_solar, metal_solar, metal_solar, metal_solar,
        metal_solar, metal_solar
    ]

    assert len(evo_models) == len(age_vals) == len(metal_vals)

    # Loop through models, testing if them work
    for ii in range(len(evo_models)):
        evo = evo_models[ii]

        # Loop through metallicities
        for jj in metal_vals[ii]:
            # Loop through ages
            for kk in age_vals[ii]:
                try:
                    test = evo.isochrone(age=10**kk, metallicity=jj)
                except:
                    print('TEST FAILED: {0}, age = {1}, metal = {2}'.format(
                        evo, kk, jj))
                    pdb.set_trace()
        print('Done {0}'.format(evo))

    return
Example #3
0
def test_metallicity():
    """
    Test isochrone generation at different metallicities
    """
    # Define isochrone parameters
    logAge = np.log10(5 * 10**6.)
    AKs = 0.8
    dist = 4000
    evo_model = evolution.MISTv1()
    atm_func = atmospheres.get_phoenixv16_atmosphere
    red_law = reddening.RedLawHosek18b()
    filt_list = ['wfc3,ir,f127m', 'wfc3,ir,f139m', 'wfc3,ir,f153m']

    # Start with a solar metallicity isochrone
    metallicity = 0.0

    # Make Isochrone object, with high mass_sampling to decrease compute time
    my_iso = synthetic.IsochronePhot(logAge,
                                     AKs,
                                     dist,
                                     metallicity=metallicity,
                                     evo_model=evo_model,
                                     atm_func=atm_func,
                                     red_law=red_law,
                                     filters=filt_list,
                                     mass_sampling=10)

    # Test isochrone properties
    assert my_iso.points.meta['METAL_IN'] == 0.0
    assert os.path.exists('iso_6.70_0.80_04000_p00.fits')

    # Now for non-solar metallicity
    metallicity = -1.5

    # Make Isochrone object, with high mass_sampling to decrease compute time
    my_iso = synthetic.IsochronePhot(logAge,
                                     AKs,
                                     dist,
                                     metallicity=metallicity,
                                     evo_model=evo_model,
                                     atm_func=atm_func,
                                     red_law=red_law,
                                     filters=filt_list,
                                     mass_sampling=10)

    metal_act = np.log10(0.00047 / 0.0142)  # For Mist isochrones

    # Test isochrone properties
    assert my_iso.points.meta['METAL_IN'] == -1.5
    assert my_iso.points.meta['METAL_ACT'] == metal_act
    assert os.path.exists('iso_6.70_0.80_04000_m15.fits')

    return
Example #4
0
def animate_ages():
    # Define isochrone parameters
    dist = 4000  # distance in parsecs
    AKs = 1.0  # Ks filter extinction in mags

    logAge = np.arange(6, 9, 0.05)  # Age in log(years)

    # Define extinction law and filters
    redlaw = reddening.RedLawCardelli(3.1)  # Rv = 3.1
    evo_model = evolution.MISTv1()

    filt_list = ['nirc2,J', 'nirc2,Kp']

    plt.figure(1)

    for aa in range(len(logAge)):
        iso = synthetic.IsochronePhot(logAge[aa],
                                      AKs,
                                      dist,
                                      filters=filt_list,
                                      red_law=redlaw,
                                      evo_model=evo_model,
                                      mass_sampling=3)

        plt.clf()
        plt.plot(iso.points['m_nirc2_J'] - iso.points['m_nirc2_Kp'],
                 iso.points['m_nirc2_J'])
        plt.xlabel('J - Kp')
        plt.ylabel('J')
        plt.gca().invert_yaxis()
        plt.title('Age = 10^{0:.2f}'.format(logAge[aa]))

        plt.xlim(1, 3)
        plt.ylim(28, 6)
        plt.savefig(
            '/u/jlu/doc/present/2020_06_ucb_lunch/iso_age_{0:.2f}.png'.format(
                logAge[aa]))
Example #5
0
import numpy as np
import pandas as pd
from astropy.io import ascii
from astropy.io import fits
import matplotlib.pyplot as plt
from popstar import synthetic, evolution, atmospheres, reddening, ifmr
from popstar.imf import imf, multiplicity


# Define isochrone parameters
logAge = 9.6   # Age in log(years)
AKs = 0  # extinction in mags
dist = 1000  # distance in parsec
metallicities = [-1]  # Metallicity in [M/H]

# Define evolution/atmosphere models and extinction law
evo_model = evolution.MISTv1()
atm_func = atmospheres.get_merged_atmosphere
red_law = reddening.RedLawHosek18b()

# Also specify filters for synthetic photometry (optional). Here we use
# the HST WFC3-IR F127M, F139M, and F153M filters
filt_list = ['wfc3,ir,f127m', 'wfc3,ir,f139m', 'wfc3,ir,f153m']

# Make Isochrone object. Note that is calculation will take a few minutes, unless the
# isochrone has been generated previously.
for metallicity in metallicities:
    my_iso = synthetic.IsochronePhot(logAge, AKs, dist, metallicity=metallicity,
                                     evo_model=evo_model, atm_func=atm_func, red_law=red_law, filters=filt_list)
    print(my_iso.save_file)
Example #6
0
def test_ifmr_multiplicity():
    from popstar import synthetic as syn
    from popstar import atmospheres as atm
    from popstar import evolution
    from popstar import reddening
    from popstar import ifmr
    from popstar.imf import imf
    from popstar.imf import multiplicity

    # Define cluster parameters
    logAge = 9.7
    AKs = 0.0
    distance = 1000
    cluster_mass = 1e6
    mass_sampling = 5

    # Test all filters
    filt_list = ['nirc2,Kp', 'nirc2,H', 'nirc2,J']

    startTime = time.time()

    evo = evolution.MISTv1()
    atm_func = atm.get_merged_atmosphere
    ifmr_obj = ifmr.IFMR()

    red_law = reddening.RedLawNishiyama09()

    iso = syn.IsochronePhot(logAge,
                            AKs,
                            distance,
                            evo_model=evo,
                            atm_func=atm_func,
                            red_law=red_law,
                            filters=filt_list,
                            mass_sampling=mass_sampling)

    print('Constructed isochrone: %d seconds' % (time.time() - startTime))

    # Now to create the cluster.
    imf_mass_limits = np.array([0.07, 0.5, 1, np.inf])
    imf_powers = np.array([-1.3, -2.3, -2.3])

    ##########
    # Start without multiplicity and IFMR
    ##########
    my_imf1 = imf.IMF_broken_powerlaw(imf_mass_limits,
                                      imf_powers,
                                      multiplicity=None)
    print('Constructed IMF: %d seconds' % (time.time() - startTime))

    cluster1 = syn.ResolvedCluster(iso, my_imf1, cluster_mass, ifmr=ifmr_obj)
    clust1 = cluster1.star_systems
    print('Constructed cluster: %d seconds' % (time.time() - startTime))

    ##########
    # Test with multiplicity and IFMR
    ##########
    multi = multiplicity.MultiplicityUnresolved()
    my_imf2 = imf.IMF_broken_powerlaw(imf_mass_limits,
                                      imf_powers,
                                      multiplicity=multi)
    print('Constructed IMF with multiples: %d seconds' %
          (time.time() - startTime))

    cluster2 = syn.ResolvedCluster(iso, my_imf2, cluster_mass, ifmr=ifmr_obj)
    clust2 = cluster2.star_systems
    comps2 = cluster2.companions
    print('Constructed cluster with multiples: %d seconds' %
          (time.time() - startTime))

    ##########
    # Tests
    ##########

    # Check that we have black holes, neutron stars, and white dwarfs in both.
    assert len(np.where(clust1['phase'] == 101)) > 0  # WD
    assert len(np.where(clust2['phase'] == 101)) > 0
    assert len(np.where(clust1['phase'] == 102)) > 0  # NS
    assert len(np.where(clust2['phase'] == 102)) > 0
    assert len(np.where(clust1['phase'] == 103)) > 0  # BH
    assert len(np.where(clust2['phase'] == 103)) > 0

    # Now check that we have companions that are WDs, NSs, and BHs
    assert len(np.where(comps2['phase'] == 101)) > 0
    assert len(np.where(comps2['phase'] == 102)) > 0
    assert len(np.where(comps2['phase'] == 103)) > 0

    # Make sure no funky phase designations (due to interpolation effects)
    # slipped through
    idx = np.where((clust1['phase'] > 5) & (clust1['phase'] < 101)
                   & (clust1['phase'] != 9))
    idx2 = np.where((comps2['phase'] > 5) & (comps2['phase'] < 101)
                    & (comps2['phase'] != 9))
    assert len(idx[0]) == 0

    return
Example #7
0
def test_IsochronePhot(plot=False):
    from popstar import synthetic as syn
    from popstar import evolution, atmospheres, reddening

    logAge = 6.7
    AKs = 2.7
    distance = 4000
    filt_list = ['wfc3,ir,f127m', 'nirc2,J']
    mass_sampling = 1
    iso_dir = 'iso/'

    evo_model = evolution.MISTv1()
    atm_func = atmospheres.get_merged_atmosphere
    redlaw = reddening.RedLawNishiyama09()

    startTime = time.time()
    iso = syn.IsochronePhot(logAge,
                            AKs,
                            distance,
                            evo_model=evo_model,
                            atm_func=atm_func,
                            red_law=redlaw,
                            filters=filt_list,
                            mass_sampling=mass_sampling,
                            iso_dir=iso_dir)
    endTime = time.time()
    print('IsochronePhot generated in: %d seconds' % (endTime - startTime))
    # Typically takes 120 seconds if file is regenerated.
    # Limited by pysynphot.Icat call in atmospheres.py

    assert iso.points.meta['LOGAGE'] == logAge
    assert iso.points.meta['AKS'] == AKs
    assert iso.points.meta['DISTANCE'] == distance
    assert len(iso.points) > 100

    assert 'm_nirc2_J' in iso.points.colnames

    if plot:
        plt.figure(1)
        iso.plot_CMD('mag814w', 'mag160w')

        plt.figure(2)
        iso.plot_mass_magnitude('mag160w')

    # Finally, let's test the isochronePhot file generation
    assert os.path.exists('{0}/iso_{1:.2f}_{2:4.2f}_{3:4s}_p00.fits'.format(
        iso_dir, logAge, AKs,
        str(distance).zfill(5)))

    # Check 1: If we try to remake the isochrone, does it read the file rather than
    # making a new one
    iso_new = syn.IsochronePhot(logAge,
                                AKs,
                                distance,
                                evo_model=evo_model,
                                atm_func=atm_func,
                                red_law=redlaw,
                                filters=filt_list,
                                mass_sampling=mass_sampling,
                                iso_dir=iso_dir)

    assert iso_new.recalc == False

    # Check 2: If we change evo model, atmo model, or redlaw,
    # does IsochronePhot regenerate the isochrone and overwrite the existing one?
    evo2 = evolution.MergedBaraffePisaEkstromParsec()
    mass_sampling = 20

    iso_new = syn.IsochronePhot(logAge,
                                AKs,
                                distance,
                                evo_model=evo2,
                                atm_func=atm_func,
                                red_law=redlaw,
                                filters=filt_list,
                                mass_sampling=mass_sampling,
                                iso_dir=iso_dir)

    assert iso_new.recalc == True

    redlaw2 = reddening.RedLawHosek18b()
    iso_new = syn.IsochronePhot(logAge,
                                AKs,
                                distance,
                                evo_model=evo2,
                                atm_func=atm_func,
                                red_law=redlaw2,
                                filters=filt_list,
                                mass_sampling=mass_sampling,
                                iso_dir=iso_dir)

    assert iso_new.recalc == True

    atm2 = atmospheres.get_castelli_atmosphere
    iso_new = syn.IsochronePhot(logAge,
                                AKs,
                                distance,
                                evo_model=evo2,
                                atm_func=atm2,
                                red_law=redlaw2,
                                filters=filt_list,
                                mass_sampling=mass_sampling,
                                iso_dir=iso_dir)

    assert iso_new.recalc == True

    return
Example #8
0
def test_cluster_mass():
    from popstar import synthetic as syn
    from popstar import atmospheres as atm
    from popstar import evolution
    from popstar import reddening
    from popstar import ifmr
    from popstar.imf import imf
    from popstar.imf import multiplicity

    # Define cluster parameters
    logAge = 6.7
    AKs = 2.4
    distance = 4000
    cluster_mass = 10**5.
    mass_sampling = 5

    # Test filters
    filt_list = ['nirc2,J', 'nirc2,Kp']

    startTime = time.time()

    # Define evolution/atmosphere models and extinction law
    evo = evolution.MISTv1()
    atm_func = atmospheres.get_merged_atmosphere
    red_law = reddening.RedLawHosek18b()

    iso = syn.IsochronePhot(logAge,
                            AKs,
                            distance,
                            evo_model=evo,
                            atm_func=atm_func,
                            red_law=red_law,
                            filters=filt_list,
                            mass_sampling=mass_sampling)

    print('Constructed isochrone: %d seconds' % (time.time() - startTime))

    # Now to create the cluster.
    imf_mass_limits = np.array([0.2, 0.5, 1, 120.0])
    imf_powers = np.array([-1.3, -2.3, -2.3])

    # IFMR
    my_ifmr = ifmr.IFMR()

    ##########
    # Start without multiplicity
    ##########
    my_imf1 = imf.IMF_broken_powerlaw(imf_mass_limits,
                                      imf_powers,
                                      multiplicity=None)
    print('Constructed IMF: %d seconds' % (time.time() - startTime))

    cluster1 = syn.ResolvedCluster(iso, my_imf1, cluster_mass, ifmr=my_ifmr)
    clust1 = cluster1.star_systems
    print('Constructed cluster: %d seconds' % (time.time() - startTime))

    # Check that the total mass is within tolerance of input mass
    cluster_mass_out = clust1['systemMass'].sum()
    assert np.abs(cluster_mass_out -
                  cluster_mass) < 200.0  # within 200 Msun of desired mass.
    print('Cluster Mass: IN = ', cluster_mass, " OUT = ", cluster_mass_out)

    ##########
    # Test with multiplicity
    ##########
    multi = multiplicity.MultiplicityUnresolved()
    my_imf2 = imf.IMF_broken_powerlaw(imf_mass_limits,
                                      imf_powers,
                                      multiplicity=multi)
    print('Constructed IMF with multiples: %d seconds' %
          (time.time() - startTime))

    cluster2 = syn.ResolvedCluster(iso, my_imf2, cluster_mass, ifmr=my_ifmr)
    clust2 = cluster2.star_systems
    print('Constructed cluster with multiples: %d seconds' %
          (time.time() - startTime))

    # Check that the total mass is within tolerance of input mass
    cluster_mass_out = clust2['systemMass'].sum()
    assert np.abs(cluster_mass_out -
                  cluster_mass) < 200.0  # within 200 Msun of desired mass.
    print('Cluster Mass: IN = ', cluster_mass, " OUT = ", cluster_mass_out)

    return
    def __init__(self,
                 age=3.9e6,
                 ext=2.63,
                 dist=7.971e3,
                 met=0.0,
                 phase=None,
                 use_atm_func='merged'):
        log_age = np.log10(age)

        self.log_age = log_age
        self.A_Ks = ext
        self.dist = dist
        self.met = met

        # Evolution/Atmosphere Models
        evo_model = evolution.MISTv1()

        if use_atm_func == 'merged':
            atm_func = atmospheres.get_merged_atmosphere
        elif use_atm_func == 'castelli':
            atm_fun = atmospheres.get_castelli_atmosphere
        elif use_atm_func == 'phoenix':
            atm_func = atmospheres.get_phoenixv16_atmosphere

        # Extinction law
        red_law = reddening.RedLawNoguerasLara18()
        self.ext_alpha = 2.30

        ## Calculate extinctions implied by isochrone extinction
        self.A_Lp = self.A_Ks * (lambda_Ks / lambda_Lp)**self.ext_alpha
        self.A_Kp = self.A_Ks * (lambda_Ks / lambda_Kp)**self.ext_alpha
        self.A_H = self.A_Ks * (lambda_Ks / lambda_H)**self.ext_alpha

        # Create an isochrone with the given parameters
        self.iso_curAge = synthetic.IsochronePhot(self.log_age,
                                                  self.A_Ks,
                                                  self.dist,
                                                  evo_model=evo_model,
                                                  atm_func=atm_func,
                                                  red_law=red_law,
                                                  metallicity=self.met,
                                                  filters=self.filt_list)

        ## Create another isochrone for absolute mags / passband luminosities
        self.iso_absMag = synthetic.IsochronePhot(self.log_age,
                                                  0.0,
                                                  10.0,
                                                  evo_model=evo_model,
                                                  atm_func=atm_func,
                                                  red_law=red_law,
                                                  metallicity=self.met,
                                                  filters=self.filt_list)

        # Save out specific stellar parameter columns needed
        ## If needing specific phase, draw it out before saving
        if phase is not None:
            phase_check = np.where(
                self.iso_curAge.points['phase'] == mist_phase_dict[phase])
        else:
            phase_check = np.where(self.iso_curAge.points['phase'] >= -1)

        self.iso_mass_init = (self.iso_curAge.points['mass'][phase_check]).to(
            u.solMass)
        self.iso_mass = self.iso_curAge.points['mass_current'][phase_check]
        self.iso_rad = (self.iso_curAge.points['R'][phase_check]).to(u.solRad)
        self.iso_lum = self.iso_curAge.points['L'][phase_check]
        self.iso_teff = self.iso_curAge.points['Teff'][phase_check]
        self.iso_logg = self.iso_curAge.points['logg'][phase_check]

        self.iso_mag_Lp = self.iso_curAge.points['m_nirc2_Lp'][phase_check]
        self.iso_mag_Kp = self.iso_curAge.points['m_nirc2_Kp'][phase_check]
        self.iso_mag_H = self.iso_curAge.points['m_nirc2_H'][phase_check]

        ## Stellar parameters from the absolute magnitude isochrones
        self.iso_absMag_mass_init = self.iso_absMag.points['mass'][phase_check]
        self.iso_absMag_mass = self.iso_absMag.points['mass_current'][
            phase_check]
        self.iso_absMag_rad = (self.iso_absMag.points['R'][phase_check]).to(
            u.solRad)
        self.iso_absMag_Lp = self.iso_absMag.points['m_nirc2_Lp'][phase_check]
        self.iso_absMag_Kp = self.iso_absMag.points['m_nirc2_Kp'][phase_check]
        self.iso_absMag_H = self.iso_absMag.points['m_nirc2_H'][phase_check]

        ## Maximum bounds on the radius in isochrone
        self.iso_rad_min = np.min(self.iso_rad).value
        self.iso_rad_max = np.max(self.iso_rad).value

        ## Maximum bounds on the initial mass in isochrone
        self.iso_mass_init_min = np.min(self.iso_mass_init).value
        self.iso_mass_init_max = np.max(self.iso_mass_init).value