Example #1
0
class Dotter2016(Isochrone):
    """ MESA isochrones from Dotter 2016:
    http://waps.cfa.harvard.edu/MIST/interp_isos.html
    """
    _dirname = os.path.join(get_iso_dir(), '{survey}', 'dotter2016')

    defaults = (Isochrone.defaults) + (
        ('dirname', _dirname, 'Directory name for isochrone files'),
        ('hb_stage', 3, 'Horizontal branch stage name'),
        ('hb_spread', 0.1, 'Intrinisic spread added to horizontal branch'),
    )

    download_url = 'http://waps.cfa.harvard.edu/MIST'
    download_defaults = copy.deepcopy(mesa_defaults_10)

    abins = np.arange(1., 13.5 + 0.1, 0.1)
    zbins = np.arange(1e-5, 1e-3 + 1e-5, 1e-5)

    columns = dict(
        des=odict([(2, ('mass_init', float)), (3, ('mass_act', float)),
                   (6, ('log_lum', float)), (9, ('u', float)),
                   (10, ('g', float)), (11, ('r', float)), (12, ('i', float)),
                   (13, ('z', float)), (14, ('Y', float)),
                   (15, ('stage', float))]),
        sdss=odict([(2, ('mass_init', float)), (3, ('mass_act', float)),
                    (6, ('log_lum', float)), (9, ('u', float)),
                    (10, ('g', float)), (11, ('r', float)), (12, ('i', float)),
                    (13, ('z', float)), (14, ('stage', float))]),
        ps1=odict([(2, ('mass_init', float)), (3, ('mass_act', float)),
                   (6, ('log_lum', float)), (9, ('g', float)),
                   (10, ('r', float)), (11, ('i', float)), (12, ('z', float)),
                   (13, ('y', float)), (16, ('stage', float))]),
        lsst=odict([(2, ('mass_init', float)), (3, ('mass_act', float)),
                    (6, ('log_lum', float)), (9, ('u', float)),
                    (10, ('g', float)), (11, ('r', float)), (12, ('i', float)),
                    (13, ('z', float)), (14, ('Y', float)),
                    (15, ('stage', float))]),
    )

    def _parse(self, filename):
        """
        Reads an isochrone in the Dotter 2016 format and determines
        the age (Gyr), metallicity (Z), and creates arrays with the
        initial stellar mass and corresponding magnitudes for each
        step along the isochrone.
        """
        try:
            columns = self.columns[self.survey.lower()]
        except KeyError as e:
            logger.warning('Unrecognized survey: %s' % (self.survey))
            raise (e)

        kwargs = dict(comments='#',
                      usecols=list(columns.keys()),
                      dtype=list(columns.values()))
        data = np.genfromtxt(filename, **kwargs)

        self.mass_init = data['mass_init']
        self.mass_act = data['mass_act']
        self.luminosity = 10**data['log_lum']
        self.mag_1 = data[self.band_1]
        self.mag_2 = data[self.band_2]
        self.stage = data['stage']

        # Check where post-AGB isochrone data points begin
        self.mass_init_upper_bound = np.max(self.mass_init)
        self.index = np.nonzero(self.stage >= 4)[0][0]

        self.mag = self.mag_1 if self.band_1_detection else self.mag_2
        self.color = self.mag_1 - self.mag_2

    @classmethod
    def z2feh(cls, z):
        # Section 3.1 of Choi et al. 2016 (https://arxiv.org/abs/1604.08592)
        Z_init = z  # Initial metal abundance
        Y_p = 0.249  # Primordial He abundance (Planck 2015)
        c = 1.5  # He enrichment ratio

        Y_init = Y_p + c * Z_init
        X_init = 1 - Y_init - Z_init

        Z_solar = 0.0142  # Solar metal abundance
        Y_solar = 0.2703  # Solar He abundance (Asplund 2009)
        X_solar = 1 - Y_solar - Z_solar

        return np.log10(Z_init / Z_solar * X_solar / X_init)

    @classmethod
    def feh2z(cls, feh):
        # Section 3.1 of Choi et al. 2016 (https://arxiv.org/abs/1604.08592)
        Y_p = 0.249  # Primordial He abundance (Planck 2015)
        c = 1.5  # He enrichment ratio

        Z_solar = 0.0142  # Solar metal abundance
        Y_solar = 0.2703  # Solar He abundance (Asplund 2009)
        X_solar = 1 - Y_solar - Z_solar

        return (1 - Y_p) / ((1 + c) + (X_solar / Z_solar) * 10**(-feh))

    def query_server(self, outfile, age, metallicity):
        z = metallicity
        feh = self.z2feh(z)

        params = dict(self.download_defaults)
        params['output'] = dict_output[self.survey]
        params['FeH_value'] = feh
        params['age_value'] = age * 1e9
        if params['age_scale'] == 'log10':
            params['age_value'] = np.log10(params['age_value'])

        server = self.download_url
        url = server + '/iso_form.php'
        # First check that the server is alive
        logger.debug("Accessing %s..." % url)
        urlopen(url, timeout=2)

        #response = requests.post(url,data=params)
        q = urlencode(params).encode('utf-8')
        request = Request(url, data=q)
        response = urlopen(request)
        try:
            fname = os.path.basename(str(response.read()).split('"')[1])
        except Exception as e:
            logger.debug(str(e))
            msg = 'Output filename not found'
            raise RuntimeError(msg)

        tmpdir = os.path.dirname(tempfile.NamedTemporaryFile().name)
        tmpfile = os.path.join(tmpdir, fname)

        out = '{0}/tmp/{1}'.format(server, fname)
        cmd = 'wget --progress dot:binary %s -P %s' % (out, tmpdir)
        logger.debug(cmd)
        stdout = subprocess.check_output(cmd,
                                         shell=True,
                                         stderr=subprocess.STDOUT)
        logger.debug(stdout)

        cmd = 'unzip %s -d %s' % (tmpfile, tmpdir)
        logger.debug(cmd)
        stdout = subprocess.check_output(cmd,
                                         shell=True,
                                         stderr=subprocess.STDOUT)
        logger.debug(stdout)

        logger.debug("Creating %s..." % outfile)
        shutil.move(tmpfile.replace('.zip', '.cmd'), outfile)
        os.remove(tmpfile)

        return outfile

    @classmethod
    def verify(cls, filename, survey, age, metallicity):
        age = age * 1e9
        nlines = 14
        with open(filename, 'r') as f:
            lines = [f.readline() for i in range(nlines)]
            if len(lines) < nlines:
                msg = "Incorrect file size"
                raise Exception(msg)

            try:
                s = lines[2].split()[-2]
                assert dict_output[survey][:4] in s
            except:
                msg = "Incorrect survey:\n" + lines[2]
                raise Exception(msg)

            try:
                z = lines[5].split()[2]
                assert np.allclose(metallicity, float(z), atol=1e-3)
            except:
                msg = "Metallicity does not match:\n" + lines[5]
                raise Exception(msg)

            try:
                a = lines[13].split()[1]
                assert np.allclose(age, float(a), atol=1e-5)
            except:
                msg = "Age does not match:\n" + lines[13]
                raise Exception(msg)
Example #2
0
class Bressan2012(ParsecIsochrone):
    _dirname = os.path.join(get_iso_dir(), '{survey}', 'bressan2012')

    defaults = (Isochrone.defaults) + (
        ('dirname', _dirname, 'Directory name for isochrone files'),
        ('hb_stage', 4, 'Horizontal branch stage name'),
        ('hb_spread', 0.1, 'Intrinisic spread added to horizontal branch'),
    )

    #download_defaults = copy.deepcopy(defaults_27)
    download_defaults = copy.deepcopy(defaults_31)
    download_defaults['isoc_kind'] = 'parsec_CAF09_v1.2S'

    columns = dict(
        des=odict([
            (3, ('mass_init', float)),
            (4, ('mass_act', float)),
            (5, ('log_lum', float)),
            (10, ('g', float)),
            (11, ('r', float)),
            (12, ('i', float)),
            (13, ('z', float)),
            (14, ('Y', float)),
            (16, ('stage', int)),
        ]),
        sdss=odict([
            (3, ('mass_init', float)),
            (4, ('mass_act', float)),
            (5, ('log_lum', float)),
            (9, ('u', float)),
            (10, ('g', float)),
            (11, ('r', float)),
            (12, ('i', float)),
            (13, ('z', float)),
            (15, ('stage', int)),
        ]),
        ps1=odict([
            (3, ('mass_init', float)),
            (4, ('mass_act', float)),
            (5, ('log_lum', float)),
            (9, ('g', float)),
            (10, ('r', float)),
            (11, ('i', float)),
            (12, ('z', float)),
            (13, ('y', float)),
            (16, ('stage', int)),
        ]),
        lsst=odict([(3, ('mass_init', float)), (4, ('mass_act', float)),
                    (5, ('log_lum', float)), (9, ('u', float)),
                    (10, ('g', float)), (11, ('r', float)), (12, ('i', float)),
                    (13, ('z', float)), (14, ('Y', float)),
                    (16, ('stage', float))]),
    )

    def _parse(self, filename):
        """Reads an isochrone file in the Padova (Bressan et al. 2012)
        format. Creates arrays with the initial stellar mass and
        corresponding magnitudes for each step along the isochrone.
        """
        #http://stev.oapd.inaf.it/cgi-bin/cmd_2.7
        try:
            columns = self.columns[self.survey.lower()]
        except KeyError as e:
            logger.warning('Unrecognized survey: %s' % (self.survey))
            raise (e)

        # delimiter='\t' is used to be compatible with OldPadova...
        # ADW: This should be updated, but be careful of column numbering
        kwargs = dict(delimiter='\t',
                      usecols=list(columns.keys()),
                      dtype=list(columns.values()))
        self.data = np.genfromtxt(filename, **kwargs)

        self.mass_init = self.data['mass_init']
        self.mass_act = self.data['mass_act']
        self.luminosity = 10**self.data['log_lum']
        self.mag_1 = self.data[self.band_1]
        self.mag_2 = self.data[self.band_2]
        self.stage = self.data['stage']

        self.mass_init_upper_bound = np.max(self.mass_init)
        self.index = len(self.mass_init)

        self.mag = self.mag_1 if self.band_1_detection else self.mag_2
        self.color = self.mag_1 - self.mag_2
Example #3
0
class Marigo2017(ParsecIsochrone):
    #http://stev.oapd.inaf.it/cgi-bin/cmd_31
    #_dirname = '/u/ki/kadrlica/des/isochrones/v4/'
    _dirname = os.path.join(get_iso_dir(), '{survey}', 'marigo2017')

    defaults = (Isochrone.defaults) + (
        ('dirname', _dirname, 'Directory name for isochrone files'),
        ('hb_stage', 4, 'Horizontal branch stage name'),
        ('hb_spread', 0.1, 'Intrinisic spread added to horizontal branch'),
    )

    download_defaults = copy.deepcopy(defaults_31)
    download_defaults['isoc_kind'] = 'parsec_CAF09_v1.2S_NOV13'

    columns = dict(
        des=odict([
            (2, ('mass_init', float)),
            (3, ('mass_act', float)),
            (4, ('log_lum', float)),
            (7, ('stage', int)),
            (23, ('u', float)),
            (24, ('g', float)),
            (25, ('r', float)),
            (26, ('i', float)),
            (27, ('z', float)),
            (28, ('Y', float)),
        ]),
        sdss=odict([
            (2, ('mass_init', float)),
            (3, ('mass_act', float)),
            (4, ('log_lum', float)),
            (7, ('stage', int)),
            (23, ('u', float)),
            (24, ('g', float)),
            (25, ('r', float)),
            (26, ('i', float)),
            (27, ('z', float)),
        ]),
        ps1=odict([
            (2, ('mass_init', float)),
            (3, ('mass_act', float)),
            (4, ('log_lum', float)),
            (7, ('stage', int)),
            (23, ('g', float)),
            (24, ('r', float)),
            (25, ('i', float)),
            (26, ('z', float)),
            (27, ('y', float)),
            (28, ('w', float)),
        ]),
        lsst=odict([
            (2, ('mass_init', float)),
            (3, ('mass_act', float)),
            (4, ('log_lum', float)),
            (7, ('stage', int)),
            (23, ('u', float)),
            (24, ('g', float)),
            (25, ('r', float)),
            (26, ('i', float)),
            (27, ('z', float)),
            (28, ('Y', float)),
        ]),
    )

    def _parse(self, filename):
        """Reads an isochrone file in the Padova (Marigo et al. 2017)
        format. Creates arrays with the initial stellar mass and
        corresponding magnitudes for each step along the isochrone.
        """
        try:
            columns = self.columns[self.survey.lower()]
        except KeyError as e:
            logger.warning('Unrecognized survey: %s' % (self.survey))
            raise (e)

        kwargs = dict(usecols=list(columns.keys()),
                      dtype=list(columns.values()))
        self.data = np.genfromtxt(filename, **kwargs)
        # cut out anomalous point:
        # https://github.com/DarkEnergySurvey/ugali/issues/29
        self.data = self.data[self.data['stage'] != 9]

        self.mass_init = self.data['mass_init']
        self.mass_act = self.data['mass_act']
        self.luminosity = 10**self.data['log_lum']
        self.mag_1 = self.data[self.band_1]
        self.mag_2 = self.data[self.band_2]
        self.stage = self.data['stage']

        self.mass_init_upper_bound = np.max(self.mass_init)
        self.index = len(self.mass_init)

        self.mag = self.mag_1 if self.band_1_detection else self.mag_2
        self.color = self.mag_1 - self.mag_2
Example #4
0
class Dotter2008(Isochrone):
    """
    KCB: currently inheriting from PadovaIsochrone because there are 
    several useful functions where we would basically be copying code.
    """
    _dirname = os.path.join(get_iso_dir(), '{survey}', 'dotter2008')
    #_zsolar = 0.0163
    _zsolar = 0.0180  # Grevesse & Sauval, 1998

    # KCB: What to do about horizontal branch?
    defaults = (Isochrone.defaults) + (
        ('dirname', _dirname, 'Directory name for isochrone files'),
        ('hb_stage', 'BHeb', 'Horizontal branch stage name'),
        ('hb_spread', 0.1, 'Intrinisic spread added to horizontal branch'),
    )

    abins = np.arange(1., 13.5 + 0.1, 0.1)
    zbins = np.arange(7e-5, 1e-3 + 1e-5, 1e-5)

    download_defaults = copy.deepcopy(dartmouth_defaults)

    columns = dict(
        des=odict([
            (1, ('mass', float)),
            (4, ('log_lum', float)),
            (5, ('u', float)),
            (6, ('g', float)),
            (7, ('r', float)),
            (8, ('i', float)),
            (9, ('z', float)),
        ]),
        sdss=odict([
            (1, ('mass', float)),
            (4, ('log_lum', float)),
            (5, ('u', float)),
            (6, ('g', float)),
            (7, ('r', float)),
            (8, ('i', float)),
            (9, ('z', float)),
        ]),
        ps1=odict([
            (1, ('mass', float)),
            (4, ('log_lum', float)),
            (6, ('g', float)),
            (7, ('r', float)),
            (8, ('i', float)),
            (9, ('z', float)),
            (10, ('y', float)),
        ]),
    )

    def _parse(self, filename):
        """
        Reads an isochrone in the Dotter format and determines the 
        age (log10 yrs and Gyr), metallicity (Z and [Fe/H]), and 
        creates arrays with the initial stellar mass and 
        corresponding magnitudes for each step along the isochrone.
        http://stellar.dartmouth.edu/models/isolf_new.html
        """
        try:
            columns = self.columns[self.survey.lower()]
        except KeyError as e:
            logger.warning('Unrecognized survey: %s' % (survey))
            raise (e)

        kwargs = dict(comments='#',
                      usecols=list(columns.keys()),
                      dtype=list(columns.values()))
        self.data = np.genfromtxt(filename, **kwargs)

        # KCB: Not sure whether the mass in Dotter isochrone output
        # files is initial mass or current mass
        self.mass_init = self.data['mass']
        self.mass_act = self.data['mass']
        self.luminosity = 10**self.data['log_lum']
        self.mag_1 = self.data[self.band_1]
        self.mag_2 = self.data[self.band_2]
        self.stage = np.tile('Main', len(self.data))

        # KCB: No post-AGB isochrone data points, right?
        self.mass_init_upper_bound = np.max(self.mass_init)

        self.mag = self.mag_1 if self.band_1_detection else self.mag_2
        self.color = self.mag_1 - self.mag_2

    @classmethod
    def z2feh(cls, z):
        # Section 3 of Dotter et al. 2008
        # Section 2 of Dotter et al. 2007 (0706.0847)
        Z_init = z  # Initial metal abundance
        Y_p = 0.245  # Primordial He abundance (WMAP, 2003)
        c = 1.54  # He enrichment ratio

        Y_init = Y_p + c * Z_init
        X_init = 1 - Y_init - Z_init

        # This is not well defined...
        #Z_solar/X_solar = 0.0229  # Solar metal fraction (Grevesse 1998)
        ZX_solar = 0.0229
        return np.log10(Z_init / X_init * 1 / ZX_solar)

    @classmethod
    def feh2z(cls, feh):
        # Section 3 of Dotter et al. 2008
        Y_p = 0.245  # Primordial He abundance (WMAP, 2003)
        c = 1.54  # He enrichment ratio

        # This is not well defined...
        #Z_solar/X_solar = 0.0229  # Solar metal fraction (Grevesse 1998)
        ZX_solar = 0.0229
        return (1 - Y_p) / ((1 + c) + (1 / ZX_solar) * 10**(-feh))

    def query_server(self, outfile, age, metallicity):
        z = metallicity
        feh = self.z2feh(z)

        params = copy.deepcopy(self.download_defaults)
        params['age'] = age
        params['feh'] = '%.6f' % feh
        params['clr'] = dict_clr[self.survey]

        url = 'http://stellar.dartmouth.edu/models/isolf_new.php'
        query = url + '?' + urlencode(params)
        logger.debug(query)
        response = urlopen(query)
        page_source = response.read()
        try:
            file_id = int(page_source.split('tmp/tmp')[-1].split('.iso')[0])
        except Exception as e:
            logger.debug(str(e))
            msg = 'Output filename not found'
            raise RuntimeError(msg)

        infile = 'http://stellar.dartmouth.edu/models/tmp/tmp%s.iso' % (
            file_id)
        command = 'wget -q %s -O %s' % (infile, outfile)
        subprocess.call(command, shell=True)

        ## ADW: Old code to rename the output file based on Zeff ([a/Fe] corrected)
        #tmpfile = tempfile.NamedTemporaryFile().name
        #tmp = open(tmpfile,'r')
        #lines = [tmp.readline() for i in range(4)]
        #z_eff = float(lines[3].split()[4])
        #basename = self.params2filename(age,z_eff)

        #logger.info("Writing %s..."%outfile)
        #mkdir(outdir)
        #shutil.move(tmpfile,outfile)

    @classmethod
    def verify(cls, filename, survey, age, metallicity):
        nlines = 8
        with open(filename, 'r') as f:
            lines = [f.readline() for i in range(nlines)]

            if len(lines) < nlines:
                msg = "Incorrect file size"
                raise Exception(msg)

            try:
                z = lines[3].split()[4]
                assert np.allclose(metallicity, float(z), atol=1e-3)
            except:
                msg = "Metallicity does not match:\n" + lines[3]
                raise Exception(msg)

            try:
                s = lines[5].split()[2]
                assert dict_output[survey][:4] in s
            except:
                msg = "Incorrect survey:\n" + lines[5]
                raise Exception(msg)

            try:
                a = lines[7].split('=')[1].strip().split()[0]
                assert np.allclose(age, float(a), atol=1e-5)
            except:
                msg = "Age does not match:\n" + lines[7]
                raise Exception(msg)